How to track down a "double free or corruption" error

337,559

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:

  1. you are deleting the same entity twice
  2. you are deleting something that wasn't allocated

For the first one I strongly suggest NULL-ing all deleted pointers.

You have three options:

  1. overload new and delete and track the allocations
  2. yes, use gdb -- then you'll get a backtrace from your crash, and that'll probably be very helpful
  3. 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:

  1. Set pointer to NULL after free
  2. Check for NULL before freeing.
  3. 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

Share:
337,559

Related videos on Youtube

neuromancer
Author by

neuromancer

Updated on July 08, 2022

Comments

  • neuromancer
    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. Could gdb make this easier?

    • sbi
      sbi about 14 years
      I 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 written delete in years. (And, yes, my code is performance-critical. Otherwise it wouldn't have been written in C++.)
    • Hasturkun
      Hasturkun about 14 years
      @sbi: Heap corruption and the like are rarely caught, at least not where they happen. NULLing pointers might make your program crash earlier.
    • sbi
      sbi about 14 years
      @Hasturkun: I strongly disagree. A major incentive to NULL pointers is to prevent a second delete ptr; from blowing up - which is masking an error, because that second delete 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
      BjornW about 3 years
      I 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
    Matthew Flaschen about 14 years
    2. 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
    Daniel Harms about 14 years
    I'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
    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
    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
    Component 10 about 14 years
    Strictly 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
    neuromancer about 14 years
    I didn't use any delete commands in my program. Could this still be the problem?
  • Component 10
    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
    Demi almost 11 years
    @Component10 I think that freeing NULL is required by the C standard to do nothing.
  • Component 10
    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
    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
    hrushi over 7 years
    Good one. True i missed to make the pointer NULL and faced with this error. Lessons learnt!
  • puk
    puk over 5 years
    Setting MALLOC_CHECK_2 actually fixed my double free problem (although it's not fixing if it's in debug mode only)
  • Wei Zhong
    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
    Kemin Zhou about 5 years
    My program takes about 30 minutes to run, on Valgrind it can take 18 to 20 hours to finish.
  • Geoffrey
    Geoffrey about 5 years
    You 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
    pellucidcoder over 3 years
    Also have it where setting MALLOC_CHECK_ avoids the problem. Fellow commenters/anyone...did you figure out a different way to exhibit the problem?
  • pellucidcoder
    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
    pellucidcoder over 3 years
    Actually.... 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
    Nikita Demodov about 3 years
    One can also use lldb if they prefer llvm over gnu.
  • tom
    tom over 2 years
    for 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 :-)