I've read this question and answer: How do malloc() and free() work?
A friend asked me how can I be sure a free worked. I replied that if it didn't work then the OS is probably crashing and it won't matter for much longer.
But I'm more interested in the nature of the answer to this question.
Once I've freed memory how can I be sure it has been freed? Do I just assume it did?
This is a purely theoretical question with no actual code behind it, and mostly when thinking about I decided "well it doesn't matter anyway", but I'm uneasy with this answer.
For instance if memory is a problem and I want to make sure that a large structure was freed after calling free, otherwise I'll try cleaning up again, is there a way to do this?
Edit:
To those stating my question answered here: Function free() in C isn't working for me
The answer provided there simply states that I "cannot actually test if free() worked". I'm trying to understand why this cannot be done. What is the nature behind free.
Edit2:
After reading through the answers provided it seems that the answer I gave my friend is accepted, that "it just works".
The only way free "doesn't work" is if you pass it an invalid pointer or one that's already been freed. Doing so invokes undefined behavior.
From the man page:
The free() function frees the memory space pointed to by ptr, which
must have been returned by a previous call to malloc(), calloc() or
realloc(). Otherwise, or if free(ptr) has already been called before,
undefined behavior occurs. If ptr is NULL, no operation is performed.
If you run your program under a memory checking tool like valgrind, it will tell you if you're calling free incorrectly.
if memory is a problem and I want to make sure that a large structure was freed after calling free, otherwise I'll try cleaning up again, is there a way to do this?
No, there is no legal way to free the same memory twice. In fact, calling free on a freed pointer leads to undefined behavior. That is why you should set freed pointers to NULL if they are to remain in scope.
There is also no API to see if a memory address has been freed or not, so your code must keep track of what is currently allocated in order to free it at some later point.
You can only do so much returning the memory to the operating system: once you call free, it's out of your hands, so you need to rely on the OS and on the memory manager of your C library to account for the freed memory correctly.
The best you can do on your side is to run your code through a memory profiler under its typical workload, and fix all errors.
You cannot verify it for specific memory allocation. There is no system call to ask if an allocation is still allocated or has already been freed.
However, as memory management and in particular memory leaks (allocations not freed), references to already freed memory, double frees and memory corruption are a serious issue in all programming languages with explicit memory management, there are tools to debug these kind of problems, such as Purify, Insure++, Valgrind or Boundschecker.
free does not return any value, or set errno to be anything (barring whatever implementation-specific features your compiler might have). The documentation for free says:
The free() function frees the memory space pointed to by ptr, which must have been returned by a previous call to malloc(), calloc() or realloc(). Otherwise, or if free(ptr) has already been called before, undefined behavior occurs. If ptr is NULL, no operation is performed.
If you call free on memory that was not allocated using the malloc family of functions, the result is undefined. If your program crashes on calling free (or operator delete in C++), it's a sign of poor memory management elsewhere in your code.
as mentioned above free doesn't return any value but just to verify I initialized one of the members from the structure & printed it before and after using free.(FYI- this is just an experiment)
struct BookList {
int BookNumber;
char BookName[4];
struct BookList * ptr_next;
}BookListVar,*ptr_BookList;
ptr_current = (struct ptr_BookList *)malloc(sizeof(struct BookList));
ptr_current->BookNumber = 123;
printf("BookNumber Stored : %d\n", ptr_current->BookNumber); //got 123 as output
free(ptr_current);
printf("BookNumber Stored : %d\n", ptr_current->BookNumber); //Got some garbage value
Related
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.
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 ;)
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.
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.
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.
As per my understanding,
free() is used to deallocate the memory that we allocated using malloc before.
In my following snippet, I have freed the memory i have allocated. But i was able to access the pointer even after freeing? How it is possible?
How free works internally?
#include<iostream>
using namespace std;
int main()
{
int *p=(int *)malloc(sizeof(int));
*p=17;
free(p);
*p=*p+1;
printf("\n After freeing memory :: %d ",*p );
return 0;
}
You can certainly continue to use p after calling free(p) and nothing will stop you. However the results will be completely undefined and unpredictable. It works by luck only. This is a common programming error called "use after free" which works in many programs for literally years without "problems" -- until it causes a problem.
There are tools which are quite good at finding such errors, such as Valgrind.
Accessing a dangling pointer will result in undefined behavior.
A dangling pointer is the one which is already freed.
After free(p), p is a dangling pointer which points to no where. free() just releases the memory block allocated by malloc() and it doesn't change the value of pointer pointing to heap in that process address space. On some platforms you might get segfault if you try dereferencing pointer after freeing. Its good practice if you assign pointer p to NULL after freeing.
In most systems and standard libraries, malloc is optimized by allocating a larger chunk of memory than required (up to 64K on some systems) from the OS, and then internally disbursing it as per requirement from this pool. The same applied to the deallocation (free) as well, in that, the free'd memory isn't freed, but put back in the memory pool, in case another request comes in, in which case the pool is reused.
Thus, the call to free hasn't actually freed the memory from the process's address space and is thus accessible even after free.
My understanding as to why this is done is that system calls are expensive, so the initial call to malloc will make a system call for enough memory that future requests for malloc do not immediately trigger another system call for more memory.
As for further reading, please take a look at this page on Wikipedia, How do malloc() and free() work? and How is malloc() implemented internally?
First, a little background so you won't think I'm attempting to do something insane:
I'm trying to debug a crash in a C library that was written by someone else. The crash looks like this:
TheProgram(44365,0x7fff75996310) malloc: *** error for object 0x7fb8d4b9d440: pointer being freed was not allocated
The crash occurs on a system where I can't run valgrind, alas. The first thing I did was wrap debug-print-macros around all of the library's calls to malloc(), calloc(), realloc(), and free() so that I see printf() output whenever memory is allocated/reallocated/freed by the library. From that debug output, it appears that the pointer that makes free() crashing was indeed allocated previously in the program, and that it wasn't freed before the problem free() call:
JJ_CHECK_MALLOC at [fastgr.c : 265] malloc() returned 0x7fb8d4b9d440
[...]
JJ_CHECK_FREE at [dotinit.c : 204] about to call free(0x7fb8d4b9d440)
TheProgram(44365,0x7fff75996310) malloc: *** error for object 0x7fb8d4b9d440: pointer being freed was not allocated
So presumably what must be happening is that somewhere after the call to malloc() and before the call to free(), the heap must be getting corrupted in such a way that free() no longer thinks that that pointer is valid.
What I need to do, then, is track down the routine that is causing the heap corruption. One way I could do this is to query the validity of the pointer at various places along the execution path, and narrow down where its status changes from "valid heap pointer" to "the heap manager doesn't know what this pointer is". But the only way I know of to find out whether the heap manager thinks the pointer is free-able is to call free(), which I obviously can't do while the program still wants to use the pointer. Is there some way to call the check-if-pointer-is-in-heap function that free() uses, without actually freeing the data?
In general: No.
There are "debugging heaps" which surround allocated blocks with additional "fence" information to help detect bad-pointer errors. These will fairly reliably complain if you try to free something that wasn't allocated through them, since the fences will be missing. (They'll also complain if you've overwritten the end of the buffer and damaged the fence, of course.) In environments where code changed frequently, I've sometimes run with these heaps permanently enabled despite the performance costs... but one would hope that they could normally be turned off before the code ships to customers.
Suppose that I have used a free() function to free a memory that,for many reasons, I'm not allowed to.
How can I stop my C application from crashing and just generate an error and continue the execution? I don't have try-catch kind of provision here (like C++/java...). Is there any way to ignore this error and continue execution?
If yes,
How do you do that?
More importantly, is it advisable to do so (continuing execution considering this memory error occurred)?
Thank you
It's certainly not advisable. Even if your program's version of free correctly detects that the memory you're trying to free cannot be freed, and thus doesn't do any harm itself, you still have a bug in your program — part of the program thought it owned that memory. Who knows what it might have tried to do with that memory before freeing it? Find and fix the bug. Don't just sweep it under the rug.
There is nothing in the C standard that you can use to do what you want. The description of the free function is very clear on that (§7.20.3.2 in C99):
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.
Freeing invalid memory is a serious bug and should be fixed, as it's possible to corrupt the state of your program. Try using a tool like valgrind to spot what's going wrong.
The only pointers you should be using free on are those you receive from malloc, calloc or realloc or NULL pointers. Further you shouldn't use free on the same pointer more than once.
I'm supporting some c code on Solaris, and I've seen something weird at least I think it is:
char new_login[64];
...
strcpy(new_login, (char *)login);
...
free(new_login);
My understanding is that since the variable is a local array the memory comes from the stack and does not need to be freed, and moreover since no malloc/calloc/realloc was used the behaviour is undefined.
This is a real-time system so I think it is a waste of cycles. Am I missing something obvious?
You can only free() something you got from malloc(),calloc() or realloc() function. freeing something on the stack yields undefined behaviour, you're lucky this doesn't cause your program to crash, or worse.
Consider that a serious bug, and delete that line asap.
No. This is a bug.
According to free(3)....
free() frees the memory space pointed
to by ptr, which must have been
returned by a previous call to
malloc(), calloc() or realloc().
Otherwise, or if free(ptr) has already
been called before, undefined
behaviour occurs. If ptr is NULL, no
operation is performed.
So you have undefined behavior happening in your program.
IN MOST CASES, you can only free() something allocated on the heap. See http://www.opengroup.org/onlinepubs/009695399/functions/free.html .
HOWEVER: One way to go about doing what you'd like to be doing is to scope temporary variables allocated on the stack. like so:
{
char new_login[64];
... /* No later-used variables should be allocated on the stack here */
strcpy(new_login, (char *)login);
}
...
The free() is definitely a bug.
However, it's possible there's another bug here:
strcpy(new_login, (char *)login);
If the function isn't pedantically confirming that login is 63 or fewer characters with the appropriate null termination, then this code has a classic buffer overflow bug. If a malicious party can fill login with the right bytes, they can overwrite the return pointer on the stack and execute arbitrary code. One solution is:
new_login[sizeof(new_login)-1]='\0';
strncpy(new_login, (char *)login, sizeof(new_login)-1 );
Definitely a bug. free() MUST ONLY be used for heap alloc'd memory, unless it's redefined to do something completely different, which I doubt to be the case.