3

I would like to know why in the following code the first delete won't free the memory:

#include <list>
#include <stdio.h>

struct abc {
    long a;

    abc() {
        puts("const");
    }
    ~abc() {
        puts("desc");
    }
};

int main() {

    std::list<abc*> test;

    abc* pA = new abc;
    printf("pA: 0x%lX\n", (unsigned long int)pA);
    test.push_back(pA);

    abc* pB = test.back();

    printf("pB: 0x%lX\n", (unsigned long int)pB);
    delete pB; // just ~abc()

    test.pop_back();

    delete pA; // ~abc() and free (works)

    puts("before double-free");

    delete pA; // ~abc() and second free (crash)

    return 0;
}

Output is:

const
pA: 0x93D8008
pB: 0x93D8008
desc
desc
before double-free
desc
*** glibc detected *** ./test: double free or corruption (fasttop): 0x093d8008 ***
...

I tried it with free() also but same behavior.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Zaffy
  • 16,801
  • 8
  • 50
  • 77

4 Answers4

4
delete pA; // ~abc() and free (works)

puts("before double-free");

delete pA; // ~abc() and second free (crash)

These delete statements are not needed once you write delete pB. You've a misconception that delete pB only calls the destructor. No, it calls the destructor and also deallocates the memory.

Also, since you've already written delete pB, the next two further delete expressions invoke undefined behavior, which means anything can happen : the program may or may not crash!

Have a look at these topics:

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I know but if there is double-free, program should crash but it won't. – Zaffy Jan 10 '12 at 15:59
  • 1
    @quarry: Why do you think it should crash? It's undefined behavior. – Benjamin Lindley Jan 10 '12 at 16:01
  • 1
    @quarry: No, if you try to free something that is already freed you invoke Undefined Behavior. The program may crash, or print a message, or it may cause demons to fly out of your nose. – Hasturkun Jan 10 '12 at 16:03
  • @quarry: See my answer again. I also answered that part. – Nawaz Jan 10 '12 at 16:03
  • 1
    @Nawaz I already read it. Every time I did something like double-free program crashed but I know that its not 100%. Now I have now other opinion. Thanks, this makes my problem clear. – Zaffy Jan 10 '12 at 16:11
  • @Nawaz: "anything can happen" is of course true by the standard, but given that glibc detects a double free on the third `delete`, I'm interested why it fails to detect it on the second `delete`. Is it an essential shortcoming of the safety measures in glibc, a bug in the detection code, or just lack of aggression to find as many such problems as possible, and what's the difference that matters between the two calls? glibc has its own requirements beyond those of the standard, and we can expect comprehensible behavior even though we aren't guaranteed it. – Steve Jessop Jan 10 '12 at 16:16
  • @SteveJessop : See my answer ... apparently there is some platform/compiler idiosyncrasies in-play here ... Since as has been pointed out, this is all undefined behavior, I'm guessing that there are some differences on each platform for how checking the state of a double-free is done. – Jason Jan 10 '12 at 16:24
  • @SteveJessop: You can set the environment variable `MALLOC_CHECK_` to make glibc use a less efficient error checking implementation instead of the default. setting this to 3 should make the program print a message to stderr and promptly `abort()` for double `free()` and similar cases. – Hasturkun Jan 10 '12 at 16:31
  • @Hasturkun: ah, so double-free detection is actually switched off here, but the second free causes some corruption that (on this occasion) is detected when the third free is attempted? – Steve Jessop Jan 10 '12 at 16:35
  • @SteveJessop: The default implementation does internal sanity/consistency checks in general. The error checking implementation adds additional checks around allocation calls to detect (and possibly ignore/survive) some measure of heap corruption (eg. using a magic value to detect `free` after writing one off the end of an allocated buffer, or freeing an invalid pointer) – Hasturkun Jan 10 '12 at 17:19
2

Your first delete renders the pointer to invalid state. So use of that pointer now leads to undefined behaviour.

int* p = new int;
delete p;
delete p; //undefined behaviour

This is guaranteed to be fine by the standard (as pointed out in the first comment):

int* p = new int;
delete p;
p = 0;
delete p; //fine
Alex Kremer
  • 1,876
  • 17
  • 18
1

First, you can't free memory allocated with new, you have to use delete.

Secondly, just because glibc doesn't detect the double free immediately, how do you know delete pB; isn't freeing it? That's what delete does, and from your own logging you're passing the same address to delete each time.

Thirdly: what are you actually trying to accomplish?

Useless
  • 64,155
  • 6
  • 88
  • 132
1

It's just an indiosyncracy of your compiler/platform that you had to call delete twice on pA before glibc complained ... for instance, on my current platform, which is OSX 10.6.8 with gcc version i686-apple-darwin10-gcc-4.2.1, I get the following output:

const
pA: 0x100100080
pB: 0x100100080
desc
desc
test(14410) malloc: *** error for object 0x100100080: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap

So you can see the first call to delete on pA, which by the standard results in undefined behavior since you didn't set the pointer to NULL or 0, did get caught as an attempt to deallocate already deallocated memory by the C++ run-time on my platform.

Since the results of the double-delete are "undefined behavior", it's really up to the implementation and platform on what happens.

You may be able to detect allocation/deallocation memory errors quicker if you use the link against the glibc memory consistency checker by compiling with the -lmcheck flag.

Jason
  • 31,834
  • 7
  • 59
  • 78