19

I have been taught in lectures, that calling free() on a pointer twice is really, really bad. I know that it is good practice, to set a pointer to NULL, right after having freed it.

However, I still have never heard any explanation as to why that is. From what I understand, the way malloc() works, it should technically keep track of the pointers it has allocated and given you to use. So why does it not know, whether a pointer it receives through free() has been freed yet or not?

I would love to understand, what happens internally, when you call free() on a location that has previously already been freed.

Sourav Ghosh
  • 127,934
  • 16
  • 167
  • 234
Joe
  • 193
  • 1
  • 1
  • 7
  • the pointer is just where you anotate the direccion of the data, but what you actually free is the data itself – Netwave Dec 15 '15 at 09:01
  • @DanielSanchez Yes, but since malloc tosses you that pointer, shouldn't the memory freeing/allocating construct still notice, that it has freed the location that pointer points to before? – Joe Dec 15 '15 at 09:03
  • If you don't use the pointer after you `free` it there's no use in setting it to `NULL`. And *if* you use the pointer after you `free` it you have *undefined behavior* no matter if you set it to `NULL` or not. Of course, if you *check* for `NULL` then it helps, but the need to set a pointer to `NULL` is not something you absolutely must do, do it on a case-to-case basis depending on how you use the pointer. – Some programmer dude Dec 15 '15 at 09:04
  • A common way to implement malloc is to store a *free blocks list*. If you add a block to this list that's already in it, hilarity will ensue. For example a future allocation might find the old entry for that block and allocate it, and then you are in a state of the block being both allocated, and in the free blocks list, so it may get allocated again. – M.M Dec 15 '15 at 09:06
  • 2
    It *could* search to see if the freed block is already in the free blocks list but this would slow down programs that don't need that functionality – M.M Dec 15 '15 at 09:07
  • @Joe It isn't required to notice that. Since free() is not required to notice it, the people implementing free() might not have put any effort into doing just that, and bad things happen if you do try to free() the same pointer twice, since you provoke a situation in the implementation of free() that noone have created any code to handle. – nos Dec 15 '15 at 09:07
  • 1
    You can look at the heap memory managed by `malloc` and `free` as a pool of water. When you allocate memory using `malloc` you get a scoop of the water and you can do with it what you want. When you `free` the memory the scoop of water if poured back into the pool and you loose track of which memory was yours, and so does the memory manager. Freeing the memory just empties your scoop, but you still keep the actual scooper (pointer). – Some programmer dude Dec 15 '15 at 09:07
  • @Joe No, malloc just return the data start memory addres, it does not handle the pointer itself, malloc just take enough memory to allocate your data, and free just frees that amount, free does not return anything and dont handle with the pointer too. So the pointer is still pointing to some address but after the free there is just garbage or even other data in that address – Netwave Dec 15 '15 at 09:07
  • See [Understanding the heap by breaking it](https://www.blackhat.com/presentations/bh-usa-07/Ferguson/Presentation/bh-usa-07-ferguson.pdf) – vlp Dec 15 '15 at 20:15
  • `malloc` will never return NULL if the pointer was properly allocated. before calling `free`, you check if the pointer is NULL. If it is, you don't `free` it. After calling `free`, you set the pointer to NULL. This ensures that if you accidentally try to `free` it again, your NULL check will prevent you from doing so. – Robert Crovella Dec 17 '15 at 07:59

4 Answers4

27

When you use malloc you are telling the PC that you want to reserve some memory location on the heap just for you. The computer gives back a pointer to the first byte of the addressed space.

When you use free you are actually telling the computer that you don't need that space anymore, so it marks that space as available for other data.

The pointer still points to that memory address. At this point that same space in the heap can be returned by another malloc call. When you invoke free a second time, you are not freeing the previous data, but the new data, and this may not be good for your program ;)

enrico.bacis
  • 27,413
  • 7
  • 76
  • 108
5

To answer your first question,

So why does it not know, whether a pointer it receives through free() has been freed yet or not?

because, the specification for malloc() in C standard does not mandate this. When you call malloc() or family of functions, what it does is to return you a pointer and internally it stores the size of the memory location allocated in that pointer. That is the reason free() does not need a size to clean up the memory.

Also, once free()-d, what happens with the actually allocated memory is still implelentation dependent. Calling free() is just a marker to point out that the allocated memory is no longer in use by the process and can be reclaimed and e re-allocated, if needed. So, keeping track of the allocated pointer is very needless at that point. It will be an unnecessary burden on the OS to keep all the backtracks.

For debugging purpose, however, some library implementations can do this job for you, like DUMA or dmalloc and last but not the least, memcheck tool from Valgrind.

Now, technically, the C standard does not specify any behaviour if you call free() on an already free-ed pointer. It is undefined behavior.

C11, chapter §7.22.3.3, free() function

[...] if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free() or realloc(), the behavior is undefined.

Sourav Ghosh
  • 127,934
  • 16
  • 167
  • 234
2

When you are calling malloc you are getting a pointer. The runtime library needs to keep track of the malloced memory. Typically malloc does not store the memory management structures separated from the malloc ed memory but in one place. So a malloc for x bytes in fact takes x+n bytes, where one possible layout is that the first n bytes are containing a linked list struct with pointers to the next (and maybe previous) allocated memory block.

When you free a pointer then the function free could walk through it's internal memory management structures and check if the pointer you pass in is a valid pointer that was malloced. Only then it could access the hidden parts of the memory block. But doing this check would be very time consuming, especially if you allocate a lot. So free simply assumes that you pass in a valid pointer. That means it directly access the hidden parts of the memory block and assumes that the linked list pointers there are valid.

If you free a block twice then you might have the problem that someone did a new malloc, got the memory you just freed, overwrites it and the second free reads invalid pointers from it.

Setting a freed pointer to NULL is good practice because it helps debugging. If you access freed memory your program might crash, but it might also just read suspicious values and maybe crash later. Finding the root cause then might be hard. If you set freed pointers to NULL your program will immediately crash when you try to access the memory. That helps massively during debugging.

Werner Henze
  • 15,279
  • 12
  • 41
  • 62
2

C standard only says that calling free twice on a pointer returned by malloc and its family function invoke undefined behavior. There is no further explanation why it is so.
But, why it is bad is explained here:

Freeing The Same Chunk Twice

To understand what this kind of error might cause, we should remember how the memory manager normally works. Often, it stores the size of the allocated chunk right before the chunk itself in memory. If we freed the memory, this memory chunk might have been allocated again by another malloc() request, and thus this double-free will actually free the wrong memory chunk - causing us to have a dangling pointer somewhere else in our application. Such bugs tend to show themselves much later than the place in the code where they occured. Sometimes we don't see them at all, but they still lurk around, waiting for an opportunity to rear their ugly heads.

Another problem that might occure, is that this double-free will be done after the freed chunk was merged together with neighbouring free chunks to form a larger free chunk, and then the larger chunk was re-allocated. In such a case, when we try to free() our chunk for the 2nd time, we'll actually free only part of the memory chunk that the application is currently using. This will cause even more unexpected problems.

haccks
  • 97,141
  • 23
  • 153
  • 244