How to track down a "double free or corruption" error
Solution 1
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.
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.
see the man page for malloc()
for more information
Solution 2
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.
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...
Solution 3
You can use gdb, but I would first try Valgrind. See the quick start guide.
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.
Solution 4
Three basic rules:
- Set pointer to
NULL
after free - Check for
NULL
before freeing. - Initialise pointer to
NULL
in the start.
Combination of these three works quite well.
Solution 5
You can use valgrind
to debug it.
#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
Related videos on Youtube
neuromancer
Updated on July 08, 2022Comments
-
neuromancer almost 2 years
When I run my (C++) program it crashes with this error.
* glibc detected * ./load: double free or corruption (!prev): 0x0000000000c6ed50 ***
How can I track down the error?
I tried using print (
std::cout
) statements, without success. Couldgdb
make this easier?-
sbi about 14 yearsI wonder why everybody suggests to
NULL
pointers (which masks errors which are otherwise caught, as this question nicely shows), but nobody suggests to simply not to do manual memory management at all, which is very well possible in C++. I haven't writtendelete
in years. (And, yes, my code is performance-critical. Otherwise it wouldn't have been written in C++.) -
Hasturkun about 14 years@sbi: Heap corruption and the like are rarely caught, at least not where they happen.
NULL
ing pointers might make your program crash earlier. -
sbi about 14 years@Hasturkun: I strongly disagree. A major incentive to
NULL
pointers is to prevent a seconddelete ptr;
from blowing up - which is masking an error, because that seconddelete
should never have been happening. (It's also used to check whether a pointer is still pointing to a valid object. But that just raises the question why you have a pointer in scope that doesn't have an object to point to.) -
BjornW about 3 yearsI think all the answers below which suggest "manually" looking for pointer problems and NULLing them etc are bad bandaids. Learning to use any of the real tools to find these problems is invaluable. In the case of Valgrind, it can be as easy as apt-get install valgrind and prefixing your program with valgrind and an option (as one of the answers below show).
-
-
Matthew Flaschen about 14 years2. would cause corruption, but I don't think this message would generally appear, since the sanity checking is only done on the heap. However, I think 3. heap buffer overflow is possible.
-
Daniel Harms about 14 yearsI'm not C expert, but I usually can keep my head above water. Why #1? Is it just so your program flat out crashes when you try and access a free'd pointer, and not just a silent error?
-
ereOn about 14 years@Precision: Yes, that's the point. It is a good practice: having a pointer to deleted memory is a risk.
-
Daniel Harms about 14 years@ereOn: Thanks. I've just had a couple classes on C, and never had to write code that with anyone else, so never really had this problem. Seems like a very good practice though.
-
Component 10 about 14 yearsStrictly speaking I think #2 is unnecessary as most compilers will allow you to try to delete a null pointer without this causing a problem. I'm sure someone will correct me if I'm wrong. :)
-
neuromancer about 14 yearsI didn't use any delete commands in my program. Could this still be the problem?
-
Component 10 about 14 years@Phenom: Why did you not use deletes? Is it because you're using smart pointers? Presumably you are using new in your code to create objects on the heap? If the answer to both of these is yes then are you using get / set on the smart pointers to copy around raw pointers? If so, don't! You'd be breaking the reference counting. Alternatively you could be assigning a pointer from library code you're calling to a smart pointer. If you don't 'own' the memory pointed to then don't do it, as the both the library and the smart pointer will try to delete it.
-
Demi almost 11 years@Component10 I think that freeing NULL is required by the C standard to do nothing.
-
Component 10 almost 11 years@Demetri: Yes, you're right "if the value of the operand of delete is the null pointer the operation has no effect." (ISO/IEC 14882:2003(E) 5.3.5.2)
-
ndemou about 8 years@SMR, in this case the essential parts of the answer is the whole, big, linked page. So including just the link in the answer is perfectly fine. A few words about why the author prefers Valgrind over gdb and how he would tackle the specific problem is IMHO what's really missing from the answer.
-
hrushi over 7 yearsGood one. True i missed to make the pointer NULL and faced with this error. Lessons learnt!
-
puk over 5 yearsSetting
MALLOC_CHECK_2
actually fixed my double free problem (although it's not fixing if it's in debug mode only) -
Wei Zhong about 5 years@puk I have the same issue, setting MALLOC_CHECK_ to 2 does avoid my double-free problem. What other options there to inject as less as code to reproduce issue and provide a backtrace?
-
Kemin Zhou about 5 yearsMy program takes about 30 minutes to run, on Valgrind it can take 18 to 20 hours to finish.
-
Geoffrey about 5 yearsYou are incorrect, this error is thrown due to a double free, exactly as the error states. The fact that you're closing a file twice is causing a double free as clearly the close method is trying to free the same data twice.
-
pellucidcoder over 3 yearsAlso have it where setting MALLOC_CHECK_ avoids the problem. Fellow commenters/anyone...did you figure out a different way to exhibit the problem?
-
pellucidcoder over 3 years" When MALLOC_CHECK_ is set to a non-zero value, a special (less efficient) implementation is used which is designed to be tolerant against simple errors, such as double calls of free with the same argument, or overruns of a single byte (off-by-one bugs)." gnu.org/software/libc/manual/html_node/… So it seems like MALLOC_CHECK_ is only used to avoid simple memory errors, not detect them.
-
pellucidcoder over 3 yearsActually.... support.microfocus.com/kb/doc.php?id=3113982 seems like setting MALLOC_CHECK_ to 3 is the most useful and can be used to detect errors.
-
Nikita Demodov about 3 yearsOne can also use lldb if they prefer llvm over gnu.
-
tom over 2 yearsfor me error was in freeing memory correctly allocated to a pointer that was correctly set up (not NULL) and the correctly freed - only once and not twice... Valgrind showed up that a different pointer had the incorrect amount of memory allocated..., which led to the error message above... - fixed the incorrect allocation for a completely different pointer and problem solved. many thanks for the helpful answer :-)