CodeQL 文档

潜在的双重释放

ID: cpp/double-free
Kind: path-problem
Security severity: 9.3
Severity: warning
Precision: high
Tags:
   - reliability
   - security
   - external/cwe/cwe-415
Query suites:
   - cpp-code-scanning.qls
   - cpp-security-extended.qls
   - cpp-security-and-quality.qls

点击查看 CodeQL 仓库中的查询

多次释放内存会导致双重释放漏洞。这可以用来破坏分配器内部数据结构,从而导致拒绝服务攻击,例如通过使程序崩溃,或者导致安全漏洞,例如允许攻击者覆盖任意内存位置。

建议

确保所有执行路径最多只释放一次分配的内存。在复杂情况下,在释放内存后将指针重新分配为 null 值可能会有所帮助。这样可以防止双重释放漏洞,因为大多数释放函数会在尝试释放内存之前执行空指针检查。

示例

在以下示例中,buff 被分配,然后被释放了两次

int* f() {
	int *buff = malloc(SIZE*sizeof(int));
	do_stuff(buff);
	free(buff);
	int *new_buffer = malloc(SIZE*sizeof(int));
	free(buff); // BAD: If new_buffer is assigned the same address as buff,
              // the memory allocator will free the new buffer memory region,
              // leading to use-after-free problems and memory corruption.
	return new_buffer;
}

通过查看上面的代码,可以通过简单地删除对 free(buff) 的额外调用来修复问题。

int* f() {
	int *buff = malloc(SIZE*sizeof(int));
	do_stuff(buff);
	free(buff); // GOOD: buff is only freed once.
	int *new_buffer = malloc(SIZE*sizeof(int));
	return new_buffer;
}

在下一个示例中,如果在第一次 delete 之后,在 try 代码块内发生异常,则可能会两次删除 task

void g() {
	MyTask *task = nullptr;

	try
	{
		task = new MyTask;

		...

		delete task;

		...
	} catch (...) {
		delete task; // BAD: potential double-free
	}
}

可以通过在第一次 delete 之后将指针分配为 null 值来解决问题,因为第二次对 null 指针调用 delete 是无害的。

void g() {
	MyTask *task = nullptr;

	try
	{
		task = new MyTask;

		...

		delete task;
		task = nullptr;

		...
	} catch (...) {
		delete task; // GOOD: harmless if task is NULL
	}
}

参考

  • ©GitHub, Inc.
  • 条款
  • 隐私