潜在的双重释放¶
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
多次释放内存会导致双重释放漏洞。这可以用来破坏分配器内部数据结构,从而导致拒绝服务攻击,例如通过使程序崩溃,或者导致安全漏洞,例如允许攻击者覆盖任意内存位置。
建议¶
确保所有执行路径最多只释放一次分配的内存。在复杂情况下,在释放内存后将指针重新分配为 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
}
}