free()
函数存在一些问题:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *a=malloc(24);
char *b=malloc(24);
free(a);
free(a);
}
有一个“ * glibc检测到* ./a.out:双重免费或损坏”。
这并不奇怪,因为我曾两次使用free()
。
但现在如果我这样做:
#include <stdio.h>
#include <stdlib.h>
int main() {
char *a=malloc(24);
char *b=malloc(24);
free(a);
free(b);
free(a);
}
我的计算机没有错误,没有双重免费错误消息,但正如您所看到的,我的代码中有双重免费...
你知道出了什么问题吗?
答案 0 :(得分:2)
我无法回答为什么在你的第二个例子中没有检测到双重自由,除了猜测libc的主要目标是默认执行。因此,检查像double-free这样的东西可能不是默认情况下库会花费大量精力进行检测的。
但是,如果运行MALLOC_CHECK_
环境变量设置为1的程序,则会对stderr
进行诊断(对于中止,将变量设置为2,对于具有诊断和中止的中止,将变量设置为3)写入stderr
}的crashdump信息:
$ gcc -o test test.c
$ ./test
$ MALLOC_CHECK_=1 ./test
*** glibc detected *** ./test: free(): invalid pointer: 0x00000000015e4010 ***
(注意环境变量名称的尾部下划线)。
"Heap Consistency Checking" page记录了使用MALLOC_CHECK_
配置的libc堆检查的行为:
检查和防范使用中的错误的另一种可能性 malloc,realloc和free是设置环境变量 MALLOC_CHECK_。设置MALLOC_CHECK_时,特殊(效率较低) 使用的实现是为了容忍简单 错误,例如使用相同参数进行双重调用,或者 单字节溢出(逐个错误)。并非所有这些错误都可以 但是,可以防止内存泄漏。如果 MALLOC_CHECK_设置为0,任何检测到的堆损坏都是静默的 忽略;如果设置为1,则在stderr上打印诊断;如果设置为2, 立即调用中止。这可能很有用,因为否则a 崩溃可能发生得更晚,问题的真正原因是 然后很难追查。
答案 1 :(得分:1)
要解决这个问题,您应该了解glibc malloc机制。如果您对此一无所知,可以阅读this以了解glibc malloc的一般知识。
Glibc使用 bins 来管理您释放的块,以避免频繁的系统调用。由于小内存空间的分配和更频繁的释放,glibc使用快速分类(等距单链表)来管理小于 global_max_fast 的内存空间(默认值) 64B或128B)。 glibc做的是将你释放的块的 fd 设置为指向快速bin指向的位置,并让快速bin指向该块。
free()使用下一个相邻块的 PREV_INUSE 位来检查是否释放了一个块。但是,当您释放的块添加到快速bin时,glibc不会设置 PREV_INUSE 位。有一段代码可以检查快速bin的指针是否与你释放的chunk的指针相同。如果是,程序会破坏,所以你不能释放指针两次,但你可以释放两个指针反过来。这是一个简短的图表,可以帮助您理解。
当你释放(a):
+++++++++++++++++++++++
+ 16 + 24 + 32 + ...
+++++++++++++++++++++++
|
|
|
|
+--->+--------+
|prevsize|
+--------+
| size |
+--------+
|fd=NULL |
+--------+
| ... |
+--------+
当你自由(b):
+++++++++++++++++++++++
+ 16 + 24 + 32 + ...
+++++++++++++++++++++++
|
|
|
|
| +--------+<---------+
| |prevsize| |
| +--------+ |
| | size | |
| +--------+ |
| |fd=NULL | |
| +--------+ |
| | ... | |
+--->+--------+ |
|prevsize| |
+--------+ |
| size | |
+--------+ |
| fd |----------+
+--------+
| ... |
+--------+
当你再次释放(a)时:
+++++++++++++++++++++++
+ 16 + 24 + 32 + ...
+++++++++++++++++++++++
|
|
|
|
+--->+--------+<---------+
|prevsize| |
+--------+ |
| size | |
+--------+ |
| fd |---+ |
+--------+ | |
| ... | | |
+--------+<--+ |
|prevsize| |
+--------+ |
| size | |
+--------+ |
| fd |----------+
+--------+
| ... |
+--------+
答案 2 :(得分:0)
使用Valgrind检测双重释放或损坏。查看有关使用valgrind Link
的博客答案 3 :(得分:0)
我的代码中存在同样的问题,在功能测试中,我尝试生成双重释放的libc异常,以测试日志记录功能。
事实证明,gcc在为Release(我认为是-O3)构建时优化了malloc()
调用和free()
调用。
我尝试在volatile
上使用a
来解决该问题,但gcc不会将volatile void*
传递给free
。然后我尝试将a
分配给volatile void* aa
,但gcc仍然优化了a
和aa
。我终于通过插入std::cout
消息来实现它。
char* a = malloc(24);
std::cout << "a is " << a << std::endl;
free(a);
free(a);
现在代码正确“崩溃”,因为我想要它,并打印出所需的双重免费消息和堆栈转储。