What is the issue with double free()? - c

I've been seeing quite a bit of erroneous code around the web and in my Advanced C class (such as the code below). While I understand that it's obviously bad coding practice, I'm having a difficult time as to what it does that's so bad other than waste CPU cycles. From my understanding so far, if a pointer allocation is not located in the "malloc table" (for lack of a better word), it is usually ignored. I'd appreciate if someone here could provide me with a better understanding as to why this is bad and (other than the fact that it's a silly thing to do) and the consequences of it are.
char* ptr = malloc(1);
...
free(ptr);
...
free(ptr);

Consider in your example after free(ptr) you did following
char* ptr = malloc(1);
free(ptr) // <-- This will delete the pointer
ptr2 = malloc(1) // <-- now you request again
now malloc is what it is and can return the same pointer as ptr and if it does and if now you do
// if you call me again and if ptr2 == ptr oops!
free(ptr)
-- your ptr2 is freed causing unexpected behavior with lots of crash, pain and hours of debugging..

This is not just a waste of CPU cycles. Double free() is undefined behaviour, which means that the program is allowed to behave in arbitrary ways.
The program might work just fine, or it might blow up in testing, or it might pass all your tests and then blow up in your customer's face, or it might corrupt some data, or it might launch a nuclear strike, etc. All of these are valid manifestations of undefined behaviour.

C11, section 7.22.3.3 says that:
[...] Otherwise, 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.

A good advise: nullify pointer after free. In your case you have undefined behaviour, because computer can allocate memory, which is pointed by ptr, or may not allocate this piece of memory, or this chunk of memory could be added to other chunk of memory during defragmentation, etc.
On the other hand, free(NULL) is defined and will do nothing.
char* ptr = malloc(1);
...
free(ptr);
ptr = NULL;
...
free(ptr);

Related

what is issue in dangling pointer?

whenever I read dangling pointers article. I can not anticipate the potential risk here.
What is the issue in below code if I do not initialize the pointer, or I do not assign ptr to NULL after deal locating malloc? Why it becomes dangling, what is the risk here?
int main()
{
int *ptr; //what is risk here?
ptr = (int *)malloc(sizeof(int));
// After below free call, ptr becomes a
// dangling pointer
free(ptr);
//ptr = NULL; //what is risk here if i do not assign NULL?
}
There is only a dangling pointer risk if you use a pointer when the data it points to is no longer valid. That is not the case in your code snippet provided.
If you do use it while the data it points to is invalid (uninitialised before the malloc or dangling after the free), all bets are off, that's undefined behaviour (UB).
It's also UB if you use the NULL that may be returned if your malloc failed, you should always check for that. And, because of that, setting it to NULL after the memory it points to ceases to be valid will not help you at all. It's just as much UB to dereference NULL as it is to dereference memory that's been freed.
You can make your code a lot safer with something like:
int main(void) {
int *ptr = malloc(sizeof(int));
if (ptr == NULL) {
puts("Could not allocate");
return 1;
}
// safe to use pointer here.
free(ptr);
// but not here.
}
The risk is not in the code shown, the risk is in the code after maintenance, modification and reuse. Or in "cargo-cult" copying of the anti-pattern. If for example you added a great deal of code between the initialised ptr declaration and the malloc(), it may no longer be clear that the pointer is not yet valid.
Dereferencing a null pointer "by accident" will normally be trapped as a run-time error, so likely to be detected during testing and development and reported at the point of error. Dereferencing a pointer that is a valid address but say refers to a block returned to the heap for re-use may have no immediate observable impact, but may later cause unrelated code to fail, possibly after deployment and in unpredictable ways - those bugs are very difficult to track down.
Dereferencing a pointer that simply has not been initialised, will have undefined behaviour, it may appear to work, it may trigger an exception, it may cause the code to appear to fail at an unrelated location in the code - again such bugs are hard to track down unless you were lucky enough for it to fail immediately - if it happened to be NULL or an invalid address triggering an exception for example.
Besides that, there are no advantages to the "unsafe" code:
int *ptr; //what is risk here?
ptr = (int *)malloc(sizeof(int));
over both safer and simpler code thus:
int* ptr = malloc( sizeof(*ptr) ) ;
Note other improvements in this:
do not cast a void-pointer to the destination type,
use the sizeof the object pointed to (or a multiple thereof) rather than an explicit type.
Remember in modern C, is is no longer necessary to declare all variables at the start of a brace-block ({...}) - though you will still see that anti-pattern too - so there are few excuses for not initialising a pointer with a useful value on instantiation.
int main()
{
int *ptr; //what is risk here?
ptr = (int *)malloc(sizeof(int));
Here you have 2 risks. The first one is that in real project between these 2 lines of code it could be a lot of another code. And there is a chance that you (or somebody else who maintains this code) will try to dereference this pointer before it's initialised.
The second one is that the return value of malloc is not checked, so in case if malloc failed, you will not know that it failed.
free(ptr);
//ptr = NULL; //what is risk here if i do not assign NULL?
When you make free() you don't really "free" the memory (you don't set all bits in allocated chunk of memory to '0') you just mark this memory as "it could be allocated again in future allocations). But your pointer is still points to this chunk of memory. So after some period of time this chunk of memory could be used for another allocation. Some new data will be stored to this chunk (data will be overwritten). And your pointer will point to the memory that stores not relevant data. And you (or worth - somebody else who maintains your project) could use this pointer without knowing that he points to non-relevant data.

malloc preventing garbage from being printed?

Program was programmed in C and compiled with GCC.
I was trying to help a friend who was trying to use trying to (shallow) copy a value that was passed into a function. His the value was a struct that held primitives and pointers (no arrays or buffers). Unsure of how malloc works, he used it similar to how the following was done:
void some_function(int rand_params, SOME_STRUCT_TYPEDEF *ptr){
SOME_STRUCT_TYPEDEF *cpy;
cpy = malloc(sizeof(SOME_STRUCT_TYPEDEF));// this line makes a difference?!?!?
cpy = ptr;// overwrites cpy anyway, right?
//prints a value in the struct documented to be a char*,
//sorry couldn't find the documentation right now
}
I told him that the malloc shouldn't affect the program, so told him to comment it out. To my surprise, the malloc caused a different output (with some intended strings) from the implementation with the malloc commented out (prints our garbage values). The pointer that's passed into the this function is from some other library function which I don't have documentation for at the moment. The best I can assume it that the pointer was for a value that was actually a buffer (that was on the stack). But I still don't see how the malloc can cause such a difference. Could someone explain how that malloc may cause a difference?
I would say that the evident lack of understanding of pointers is responsible for ptr actually pointing to memory that has not been correctly allocated (if at all), and you are experiencing undefined behaviour. The issue is elsewhere in the program, prior to the call to some_function.
As an aside, the correct way to allocate and copy the data is this:
SOME_STRUCT_TYPEDEF *cpy = malloc(sizeof(SOME_STRUCT_TYPEDEF));
if (cpy) {
*cpy = *ptr;
// Don't forget to clean up later
free(cpy);
}
However, unless the structure is giant, it's a bit silly to do it on the heap when you can do it on the stack like this:
SOME_STRUCT_TYPEDEF cpy = *ptr;
I can't see why there difference in the print.
can you show the print code?
anyway the malloc causes memory leak. you're not supposed to allocate memory for 'cpy' because pointer assignment is not shallow-copy, you simply make 'cpy' point to same memory 'ptr' point by storing the address of the start of that memory in 'cpy' (cpy is mostly a 32/64 bit value that store address, in case of malloc, it will store the address of the memory section you allocated)

I am still confused with free() and malloc()

In the below program, as far as in my knowledge once we allocate some memory then if we are chaging the address from
ptr to ptr++, then when we are calling free with ptr i.e free(ptr).
Then the program should crash.
But in this program works fine.
How it works?
I am using Code::Bocks in Windows XP.
Please help me.
int main()
{
int *ptr;
ptr = malloc(1);
*ptr = 6;
printf("The value at *ptr = %d \n", *ptr);
ptr++; //Now ptr is address has been changed
free(ptr); // Program should crash here
ptr = NULL;
/* *ptr = 5;*/ // This statement is crashing
return 0;
}
The behavior of this program is undefined from the very moment that you store a value through an int* pointing to a single byte. There's no guarantee of a crash.
Specifically, it seems like your free doesn't (maybe can't) check its argument properly. Try running this program with a malloc debugging library.
Absence of a crash does not necessarily mean your code is fine. On another platform, it will probably crash.
BTW: you should rather malloc(sizeof(int))
The program should not necessarily crash, the behavior is undefined.
It works only due to luck; the code is still wrong.
To understand why, one first needs to be familiar with the implementation of the malloc library itself. malloc not only allocates the space it returned for you to use, it also allocates space for its own metadata that it uses to manage the heap (what we call the region of memory managed by malloc). By changing ptr and passing the wrong value to free, the metadata is no longer where free expected it, so the data it's using now is essentially garbage and the heap is corrupted.
This doesn't necessarily mean it'll crash due to say, a segfault, because it doesn't necessarily dereference a bad pointer. The bug still exists and will likely manifest itself in a longer program with more heap usage.

Understanding concept of free

Tried the following code :
#include<stdio.h>
int main()
{
int *p,*q;
p = (int *)malloc(sizeof(int));
*p =10;
q = p;
printf("%u \n",p);
printf("%u \n",q);
free(p);
printf("%u \n",p);
return 0;
}
The output got is as follows :
[root#lnxdesk Tazim]# ./a.out
154804232
154804232
154804232
Why is that address inside p is still printed even if I have done free(p);?
What has free(p) done then?
I want to understand the concept of free/malloc clearly. Any help will be valuable.
free() only frees the memory on the heap. It does not change the value of your pointer. If you tried to print the memory pointed by your pointer, you'll probably get some kind of garbage.
Also, when you called free, you gave it the pointer, not the address to your pointer, so free can't change your pointer...
That's undefined behavior - once you've freed the pointer the address stored becomes invalid and you can't do anything with it - not only you can't dereference it, but you can't even printf() the pointer value.
You are printing the pointers, i.e. the address of the memory zones allocated for you ints. Freeing a memory zone with free does not set the pointer's address to 0x00 as I think you expect.
It just tells the OS that the memory zone is available again for future re-use.
If you were printing *p after free(p), you would have problems.
malloc() and its ilk reserve space in a memory storage area called the "heap" and return a pointer to that reserved area. So in your sample above p is given a pointer to, probably, a four-byte memory region that has been reserved for its use (whose address happens to be 154804232 this time around). When you do *p = 10 you are now placing the integer value 10 into the memory pointed to. When you do q = p you're now making q point to the same chunk of reserved heap memory.
free() and its ilk just unreserve the memory. When you call free() you're saying "I'm not going to use this memory anymore". All free() does is tell the memory management system that the block of memory is now available for use once again. It emphatically does not change your pointer. It just signals that the block of memory is available. After that it is up to you to ensure that you do not use that pointer again.
If you do use that pointer again it may work fine. Once. Or twice. Or a thousand times. It'll work fine, basically, until you use it after someone else claims that memory block you've said is free and does something with it. When that transpires, Bad Things Happen<tm>. Please don't make bad things happen.
Remember : a pointer is nothing but an address. Before, after your malloc, or free, it'll give you the same result. The only thing that malloc() does is reserve space at this address. The only thing that free does is release it (most probably, mark this address as usable to store other things, "cleaning" would be time consuming).
This is why putting your pointer to NULL after a free is a good idea ; because you can be sure if the pointer is connected to something or not.
free does not reassign the pointer to point to something else. In fact, the C standard
does not mention anything be done with the pointer. This is all it says in the description:
The free function causes the space
pointed to by ptr to be deallocated,
that is, made available for further
allocation. If ptr is a null pointer,
no action occurs. Otherwise, if the
argument does not match a pointer
earlier returned by the calloc,
malloc, or realloc function, or if the
space has been deallocated by a call
to free or realloc, the behavior is
undefined

What happens when you try to free() already freed memory in c?

For example:
char * myString = malloc(sizeof(char)*STRING_BUFFER_SIZE);
free(myString);
free(myString);
Are there any adverse side effects of doing this?
Here's the chapter and verse.
If the argument [to the free function] does not match a pointer earlier returned by the calloc, malloc, or
realloc function, or if the space has been deallocated by a call to free or realloc,
the behavior is undefined. (ISO 9899:1999 - Programming languages — C, Section 7.20.3.2)
One of nothing, silent memory corruption, or segmentation fault.
Yes, you can get a double free error that causes your program to crash. It has to do with malloc's internal data structures to keep track of allocated memory.
Answer summary:
Yes, bad things can and probably will happen.
To prevent this do:
free(myString);
myString = NULL;
Note that all references to the memory must be set to NULL if others were created.
Also, calling free() with a NULL results in no action. For more info see: man free
Not so clever. Google for double free vulnerabilities. Set your pointer to NULL after freeing to avoid such bugs.
It (potentially) makes demons fly out of your nose.
Depending on which system you run it on, nothing will happen, the program will crash, memory will be corrupted, or any other number of interesting effects.
Bad Things (TM)
Really, I think it's undefined so anything at all including playing "Global Thermonuclear War" with NORAD's mainframe
Always set a pointer to NULL after freeing it.
It is safe to attempt to free a null pointer.
It's worth writing your own free wrapper to do this automatically.
Don't do that. If the memory that got freed is re-allocated to something else between the calls to free, then things will get messed up.
It may crash your program, corrupt memory, or have other more subtle negative effects. After you delete memory, it is a good idea to set it to NULL (0). Trying to free a null pointer does nothing, and is guaranteed to be safe. The same holds true for delete in c++.
In short: "Undefined Behavior".
(Now, what that can include and why that is the case the others have already said. I just though it was worth mentioning the term here as it is quite common).
The admittedly strange macro below is a useful drop-in replacement for wiping out a few classes of security vulnerabilities as well as aid debugging since accesses to free()'d regions are more likely to segfault instead of silently corrupting memory.
#define my_free(x) do { free(x); x = NULL; } while (0)
The do-while loop is to help surrounding code more easily digest the multiple-statements. e.g. if (done) my_free(x);
Another interesting situation:
char * myString = malloc(sizeof(char)*STRING_BUFFER_SIZE);
char * yourString = myString;
if (myString)
{
free(myString);
myString = NULL;
}
// Now this one is safe, because we keep to the rule for
// setting pointers to NULL after deletion ...
if (myString)
{
free(myString);
myString = NULL;
}
// But what about this one:
if (yourString)
{
free(yourString);
yourString = NULL;
}
//?!? :)

Resources