C++ 如何追踪“双重释放或损坏”错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2902064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to track down a "double free or corruption" error
提问by neuromancer
When I run my (C++) program it crashes with this error.
当我运行我的 (C++) 程序时,它会因此错误而崩溃。
* glibc detected *./load: double free or corruption (!prev): 0x0000000000c6ed50 ***
* 检测到 glibc *./load: double free or corruption (!prev): 0x0000000000c6ed50 ***
How can I track down the error?
如何追踪错误?
I tried using print (std::cout
) statements, without success. Could gdb
make this easier?
我尝试使用 print ( std::cout
) 语句,但没有成功。可以gdb
简化这个过程?
回答by Hasturkun
If you're using glibc, you can set the MALLOC_CHECK_
environment variable to 2
, this will cause glibc to use an error tolerant version of malloc
, which will cause your program to abort at the point where the double free is done.
如果您正在使用 glibc,您可以将MALLOC_CHECK_
环境变量设置为2
,这将导致 glibc 使用 的容错版本malloc
,这将导致您的程序在完成双重释放时中止。
You can set this from gdb by using the set environment MALLOC_CHECK_ 2
command before running your program; the program should abort, with the free()
call visible in the backtrace.
您可以set environment MALLOC_CHECK_ 2
在运行程序之前使用该命令从 gdb 设置它;程序应该中止,free()
调用在回溯中可见。
see the man page for malloc()
for more information
有关更多信息,请参阅手册页malloc()
回答by Kornel Kisielewicz
There are at least two possible situations:
至少有两种可能的情况:
- you are deleting the same entity twice
- you are deleting something that wasn't allocated
- 您两次删除同一个实体
- 您正在删除未分配的内容
For the first one I strongly suggest NULL-ing all deleted pointers.
对于第一个,我强烈建议将所有已删除的指针设为 NULL。
You have three options:
您有三个选择:
- overload new and delete and track the allocations
- yes, use gdb -- then you'll get a backtrace from your crash, and that'll probably be very helpful
- as suggested -- use Valgrind -- it isn't easy to get into, but it will save you time thousandfold in the future...
- 重载 new 并删除和跟踪分配
- 是的,使用 gdb——然后你会从你的崩溃中得到一个回溯,这可能会很有帮助
- 正如建议的那样——使用 Valgrind——它并不容易进入,但它会在未来为你节省千倍的时间......
回答by Matthew Flaschen
You can use gdb, but I would first try Valgrind. See the quick start guide.
您可以使用 gdb,但我会先尝试Valgrind。请参阅快速入门指南。
Briefly, Valgrind instruments your program so it can detect several kinds of errors in using dynamically allocated memory, such as double frees and writes past the end of allocated blocks of memory (which can corrupt the heap). It detects and reports the errors as soon as they occur, thus pointing you directly to the cause of the problem.
简而言之,Valgrind 检测您的程序,以便它可以检测使用动态分配的内存时出现的几种错误,例如双重释放和超过分配的内存块末尾的写入(这可能会损坏堆)。它会在错误发生时立即检测并报告错误 ,从而直接指出问题的原因。
回答by Hyman
Three basic rules:
三个基本规则:
- Set pointer to
NULL
after free - Check for
NULL
before freeing. - Initialise pointer to
NULL
in the start.
- 将指针设置为
NULL
释放后 NULL
释放前检查。NULL
在开始时初始化指针。
Combination of these three works quite well.
这三者的结合效果非常好。
回答by Sandipan Karmakar
You can use valgrind
to debug it.
你可以valgrind
用来调试它。
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *x = malloc(100);
free(x);
free(x);
return 0;
}
[sand@PS-CNTOS-64-S11 testbox]$ vim t1.c
[sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1
[sand@PS-CNTOS-64-S11 testbox]$ ./t1
*** glibc detected *** ./t1: double free or corruption (top): 0x00000000058f7010 ***
======= Backtrace: =========
/lib64/libc.so.6[0x3a3127245f]
/lib64/libc.so.6(cfree+0x4b)[0x3a312728bb]
./t1[0x400500]
/lib64/libc.so.6(__libc_start_main+0xf4)[0x3a3121d994]
./t1[0x400429]
======= Memory map: ========
00400000-00401000 r-xp 00000000 68:02 30246184 /home/sand/testbox/t1
00600000-00601000 rw-p 00000000 68:02 30246184 /home/sand/testbox/t1
058f7000-05918000 rw-p 058f7000 00:00 0 [heap]
3a30e00000-3a30e1c000 r-xp 00000000 68:03 5308733 /lib64/ld-2.5.so
3a3101b000-3a3101c000 r--p 0001b000 68:03 5308733 /lib64/ld-2.5.so
3a3101c000-3a3101d000 rw-p 0001c000 68:03 5308733 /lib64/ld-2.5.so
3a31200000-3a3134e000 r-xp 00000000 68:03 5310248 /lib64/libc-2.5.so
3a3134e000-3a3154e000 ---p 0014e000 68:03 5310248 /lib64/libc-2.5.so
3a3154e000-3a31552000 r--p 0014e000 68:03 5310248 /lib64/libc-2.5.so
3a31552000-3a31553000 rw-p 00152000 68:03 5310248 /lib64/libc-2.5.so
3a31553000-3a31558000 rw-p 3a31553000 00:00 0
3a41c00000-3a41c0d000 r-xp 00000000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1
3a41c0d000-3a41e0d000 ---p 0000d000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1
3a41e0d000-3a41e0e000 rw-p 0000d000 68:03 5310264 /lib64/libgcc_s-4.1.2-20080825.so.1
2b1912300000-2b1912302000 rw-p 2b1912300000 00:00 0
2b191231c000-2b191231d000 rw-p 2b191231c000 00:00 0
7ffffe214000-7ffffe229000 rw-p 7ffffffe9000 00:00 0 [stack]
7ffffe2b0000-7ffffe2b4000 r-xp 7ffffe2b0000 00:00 0 [vdso]
ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0 [vsyscall]
Aborted
[sand@PS-CNTOS-64-S11 testbox]$
[sand@PS-CNTOS-64-S11 testbox]$ vim t1.c
[sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1
[sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck ./t1
==20859== Memcheck, a memory error detector
==20859== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==20859== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==20859== Command: ./t1
==20859==
==20859== Invalid free() / delete / delete[]
==20859== at 0x4A05A31: free (vg_replace_malloc.c:325)
==20859== by 0x4004FF: main (t1.c:8)
==20859== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd
==20859== at 0x4A05A31: free (vg_replace_malloc.c:325)
==20859== by 0x4004F6: main (t1.c:7)
==20859==
==20859==
==20859== HEAP SUMMARY:
==20859== in use at exit: 0 bytes in 0 blocks
==20859== total heap usage: 1 allocs, 2 frees, 100 bytes allocated
==20859==
==20859== All heap blocks were freed -- no leaks are possible
==20859==
==20859== For counts of detected and suppressed errors, rerun with: -v
==20859== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
[sand@PS-CNTOS-64-S11 testbox]$
[sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1
==20899== Memcheck, a memory error detector
==20899== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==20899== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==20899== Command: ./t1
==20899==
==20899== Invalid free() / delete / delete[]
==20899== at 0x4A05A31: free (vg_replace_malloc.c:325)
==20899== by 0x4004FF: main (t1.c:8)
==20899== Address 0x4c26040 is 0 bytes inside a block of size 100 free'd
==20899== at 0x4A05A31: free (vg_replace_malloc.c:325)
==20899== by 0x4004F6: main (t1.c:7)
==20899==
==20899==
==20899== HEAP SUMMARY:
==20899== in use at exit: 0 bytes in 0 blocks
==20899== total heap usage: 1 allocs, 2 frees, 100 bytes allocated
==20899==
==20899== All heap blocks were freed -- no leaks are possible
==20899==
==20899== For counts of detected and suppressed errors, rerun with: -v
==20899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
[sand@PS-CNTOS-64-S11 testbox]$
One possible fix:
一种可能的解决方法:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *x = malloc(100);
free(x);
x=NULL;
free(x);
return 0;
}
[sand@PS-CNTOS-64-S11 testbox]$ vim t1.c
[sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1
[sand@PS-CNTOS-64-S11 testbox]$ ./t1
[sand@PS-CNTOS-64-S11 testbox]$
[sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1
==20958== Memcheck, a memory error detector
==20958== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==20958== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==20958== Command: ./t1
==20958==
==20958==
==20958== HEAP SUMMARY:
==20958== in use at exit: 0 bytes in 0 blocks
==20958== total heap usage: 1 allocs, 1 frees, 100 bytes allocated
==20958==
==20958== All heap blocks were freed -- no leaks are possible
==20958==
==20958== For counts of detected and suppressed errors, rerun with: -v
==20958== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
[sand@PS-CNTOS-64-S11 testbox]$
Check out the blog on using Valgrind Link
查看有关使用 Valgrind链接的博客
回答by Sitesh
With modern C++ compilers you can use sanitizersto track.
使用现代 C++ 编译器,您可以使用消毒剂进行跟踪。
Sample example :
示例:
My program:
我的程序:
$cat d_free.cxx
#include<iostream>
using namespace std;
int main()
{
int * i = new int();
delete i;
//i = NULL;
delete i;
}
Compile with address sanitizers :
使用地址消毒剂编译:
# g++-7.1 d_free.cxx -Wall -Werror -fsanitize=address -g
Execute :
执行 :
# ./a.out
=================================================================
==4836==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0:
#0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140
#1 0x400b2c in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:11
#2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04)
#3 0x400a08 (/media/sf_shared/jkr/cpp/d_free/a.out+0x400a08)
0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014)
freed by thread T0 here:
#0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140
#1 0x400b1b in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:9
#2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04)
previously allocated by thread T0 here:
#0 0x7f35b2d7a040 in operator new(unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:80
#1 0x400ac9 in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:8
#2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04)
SUMMARY: AddressSanitizer: double-free /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 in operator delete(void*, unsigned long)
==4836==ABORTING
To learn more about sanitizers you can check thisor thisor any modern c++ compilers (e.g. gcc, clang etc.) documentations.
回答by Component 10
Are you using smart pointers such as Boost shared_ptr
? If so, check if you are directly using the raw pointer anywhere by calling get()
. I've found this to be quite a common problem.
您是否使用智能指针,例如 Boost shared_ptr
?如果是这样,请检查您是否通过调用在任何地方直接使用原始指针get()
。我发现这是一个很常见的问题。
For example, imagine a scenario where a raw pointer is passed (maybe as a callback handler, say) to your code. You might decide to assign this to a smart pointer in order to cope with reference counting etc. Big mistake: your code doesn't own this pointer unless you take a deep copy. When your code is done with the smart pointer it will destroy it and attempt to destroy the memory it points to since it thinksthat no-one else needs it, butthe calling code will then try to delete it and you'll get a double free problem.
例如,想象一个原始指针被传递给你的代码的场景(比如作为回调处理程序)。您可能决定将其分配给智能指针以应对引用计数等。大错误:除非您进行深层复制,否则您的代码不拥有此指针。当您的代码使用智能指针完成后,它将销毁它并尝试销毁它指向的内存,因为它认为没有其他人需要它,但是调用代码将尝试删除它,您将得到一个双倍免费问题。
Of course, that might not be your problem here. At it's simplest here's an example which shows how it can happen. The first delete is fine but the compiler senses that it's already deleted that memory and causes a problem. That's why assigning 0 to a pointer immediately after deletion is a good idea.
当然,这可能不是你的问题。这是最简单的例子,它展示了它是如何发生的。第一次删除没问题,但编译器感觉到它已经删除了该内存并导致问题。这就是为什么在删除后立即将 0 分配给指针是一个好主意。
int main(int argc, char* argv[])
{
char* ptr = new char[20];
delete[] ptr;
ptr = 0; // Comment me out and watch me crash and burn.
delete[] ptr;
}
Edit: changed delete
to delete[]
, as ptr is an array of char.
编辑:更改delete
为delete[]
,因为 ptr 是一个字符数组。
回答by Jason
I know this is a very old thread, but it is the top google search for this error, and none of the responses mention a common cause of the error.
我知道这是一个非常古老的线程,但它是此错误的顶级谷歌搜索,并且没有任何回复提到错误的常见原因。
Which is closing a file you've already closed.
这是关闭您已经关闭的文件。
If you're not paying attention and have two different functions close the same file, then the second one will generate this error.
如果你不注意并且有两个不同的函数关闭同一个文件,那么第二个会产生这个错误。