Fail to understand why freeing memory throws an error - c

I was reading to Dangling Pointer and found it's a good habit do this, to prevent oneself from dangling pointer error.
free(ptr); // free the ptr
ptr = NULL;
Now I decided to test this with a sample vanilla C code.
CASE_1
char *ptr = malloc(10);
...
...
free(ptr);
ptr=NULL;
// Just to check what happen if I call free more than I once
free(ptr)
ptr=NULL;
All work fine. Until I decided to wrap the free and the pointer NULL assignment in a function I named it safefree
void safefree(char *pp) {
free(pp);
pp = NULL;
}
CASE_2
Now, when I ran the above method more than 1 (like this)
safefree(ptr);
safefree(ptr);
I get the following error.
malloc: *** error for object 0x7fd98f402910: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
I happen to understand the error (pointer being freed was not allocated)
but I fail to understand why it doesn't fail in the CASE_1 and fails in latter part of the sample code.

First, let's review the behaviour of free(). Quoting C11, chapter ยง7.22.3.3
void free(void *ptr);
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 a memory management
function, or if the space has been deallocated by a call to free or realloc, the
behavior is undefined.
Follow the two emphasized points.
You can pass a null pointer, NULL, to free() as many times as you want, they are valid calls (just to be ignored).
You cannot pass a pointer which has already been passed to free() once.
Case 1:
Here, after calling free() with the pointer, we're explicitly setting the pointer to NULL. That is why, calling free() with the same pointer variable later, any number of time, is not an issue, at all.
case 2:
C uses pass-by-value for function argument passing. That's why, When wrapped inside a function,
free(pp);
works as expected, (it passes the required pointer to free()) but
pp = NULL;
is local to the function and that change is not reflected to the caller. Thereby, calling the function again causes double free, as now,
the pointer has already been passed to free()
the assigned NULL is not reflected to the caller, hence the pointer is not set to NULL
in the next call, we're passing already-free()d pointer again.
As already mentioned, this invokes undefined behavior.
Solution: You need to pass a pointer to the pointer as the argument of the called function safefree() and from the called function, you can set the pointer value to NULL to get it reflected in the caller. Something like
void safefree(void ** ptrToPtr)
{
free(*ptrToPtr);
*ptrToPtr= NULL;
}
and calling like
safefree (&ptrToBeFreed);
will do the job (mind the types there, anyways).

Aside from Jonathan's remark, if you do something that results in undefined behavior (free the pointer again), it results in undefined behavior (errors, crashes).
Doing the same thing in different contexts does not necessarily result in the same undefined behavior, it depends on implementation details; they are typically only known to the compiler developer, so they seem 'random' from the outside. Nothing much can be learned by analyzing random undefined behavior. It could for example be that the compile time affects the result..., or the spelling of the directory you work in... anything.

Related

Free causes a error : free(): invalid pointer: 0x0000000001d04018

I tried to compile the following code :
#include <stdio.h>
void main()
{
int *p = (int*) malloc(4*sizeof(int));
p++;
p++;
free(p);
}
But it gives me the following error.
* Error in `/home/a.out': free(): invalid pointer: 0x0000000001d04018 *
Aborted.
I think I am still within the allocated limits of size 4 ints. But then why invalid pointer. Can some one help.
You are invoking undefined behavior.
From free manual:
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.
It doesn't matter whether you are inside allocated region or not. The pointer must be the same that was allocated before.
The rationale for being it undefined is that typical implementation (this is implementation defined) must remember other metadata related to the allocation, e.g. the allocation size, in some data structure in order to free them later. If ptr is not same as returned by allocation function then there is no way to lookup into this data structure to get those metadata during freeing.
The free() function can only operate on a pointer that has been directly allocated with malloc() or any of its companions.
If you change the pointer you'll have errors like this, that modified pointer was never allocated.
"Close enough" does not count. It must be identical to the pointer you got in the first place.
The contract for malloc and free is fairly simple. The pointer value (the address) you pass to free must be exactly a pointer value you got from malloc.
Exactly, and not in the same block, however close. It must match exactly. You modify the pointer before passing it back (you increment), so you violate that contract. This is going to cause undefined behavior. And you experience it in the from of a crash. It could be worse, it could appear to work. Until it blows up in your face later for no apparent reasons. That's the nature of undefined behavior. Avoid it.

Decayed array and memory deallocation

I have been scratching my head regarding the memory 'behavior' with arrays decayed as pointers.
I have a function where an array of structures is created in a function (without explicit memory allocation) and then passed to another function as a pointer. Said function store the pointer in a static variable.
Reading this kind of code, I would say the pointer should be invalidated at the end of the first function (since no malloc was done) yet it is not.
However, calling a free() on this pointer throws a glibc error : invalid pointer. Makes sense, since no malloc was called.
Is there some implicit memory allocation being performed because of the decaying array ?
Is there a way to properly free the memory after this ? I know the trivial answer would be to allocate the memory myself, I am mainly asking out of curiosity.
Edit : some dummy code as requested :
static structure* s_array = NULL;
void foo()
{
structure array[5];
bar(array); // array decaying as a pointer
}
void bar(structure* ptr)
{
s_array = array; // pointer stored in the static, not invalidated at the end of foo()
}
void freeBar()
{
free(s_array); // invalid pointer
}
I would say the pointer should be invalidated at the end of the first
function (since no malloc was done) yet it is not.
Yes, it is, but "invalidation" in C just means that subsequent behavior is undefined ... anything can happen, including your program appearing to work perfectly.
However, calling a free() on this pointer throws a glibc error : invalid pointer. Makes sense, since no malloc was called.
Indeed it makes sense since malloc wasn't called. You can only free memory that was malloced; freeing memory that isn't is undefined behavior, and in this case the library was kind enough to produce a diagnostic.
Is there some implicit memory allocation being performed because of the decaying array ?
There's no such thing as a "decaying array". Arrays aren't first class objects in C so they cannot be passed to functions ... only their addresses can. The "decay" simply consists of the name of the array being converted to a pointer to the first element of the array when used in expression context ... it's a compile-time thing.
Is there a way to properly free the memory after this ?
The memory was "freed" at the end of the first function; nothing else need be done ... other than not storing its address.

C - What Happens To Memory After free()? [duplicate]

This question already has answers here:
What happens to memory after free()?
(4 answers)
Closed 9 years ago.
I have this struct type that I malloc for, and after I free it the pointer still points to the data I assigned. Is that just because the pointer is pointing to memory that is free but hasn't been reallocated yet?
#include <stdio.h>
struct S {
int value;
}
int main () {
S *s = malloc(sizeof(struct S));
s->value = 8910;
free(s);
printf("s: %i\n", s->value);
}
Freed memory doesn't belong to you anymore. But that doesn't mean it disappears or gets changed in any way. Why would your program bother? It would be a waste of time. It probably just marks the memory as available for use by subsequent malloc()s, and that's it. Or it might not. Using memory that doesn't belong to you might do anything: return wrong values, crash, return right values, or run a flight simulator game. It's not yours; don't mess with it and you'll never have to worry about what it might do.
The C standard defines the behavior of the free function:
The free function causes the space pointed to by ptr to be
deallocated, that is, made available for further allocation.
which means that a later call to malloc (or something else) might re-use the same memory space.
As soon as a pointer is passed to free(), the object it pointed to reaches the end of its lifetime. Any attempt to refer to the pointed-to object has undefined behavior (i.e., you're no longer allowed to dereference the pointer).
More than that, the value of the pointer itself becomes indeterminate, so any attempt to refer to the pointer value has undefined behavior. Reference: N1570 6.2.4p2:
If an object is referred to outside of its lifetime, the behavior is
undefined. The value of a pointer becomes indeterminate when the
object it points to (or just past) reaches the end of its lifetime.
It's true that free()'s argument is passed by value (like all C function arguments), and so free can't actually modify the pointer. One way to think of it is that the pointer has the "same" value before and after the call, but that value is valid before the call and indeterminate after the call.
It's likely that an attempt to refer to the pointer value, or even to dereference it, will appear to "work". That's one of the many possible symptoms of undefined behavior (and arguably the worst, since it makes it difficult to detect and diagnose the error).
free() just declares, to the language implementation or operating system, that the memory is no longer required. When it is written over is not defined behavior.

What does free do to a pointer passed by value to a function?

It is known that if we pass a pointer by value to a function, it cannot be freed inside the function, like so:
void func(int *p)
{
free(p);
p = NULL;
}
p holds a copy of a (presumably valid) address, so free(p) tries to, well, free it. But since it is a copy, it cannot really free it. How does the call to free() know that it cannot really free it ?
The code above does not produce an error. Does that mean free() just fails silently, "somehow" knowing that address passed in as argument cannot be worked upon ?
p holds a copy of a (presumably valid) address, so free(p) tries to, well, free it. But since it is a copy, it cannot really free it.
It's not true. free() can work just fine if p is a valid address returned by malloc() (or NULL).
In fact, this is a common pattern for implementing custom "destructor" functions (when writing OO-style code in C).
What you probably mean is that p won't change to NULL after this - but that's natural, since you're passing it by value. If you want to free() and null out the pointer, then pass it by pointer ("byref"):
void func(int **p)
{
if (p != NULL) {
free(*p);
*p = NULL;
}
}
and use this like
int *p = someConstructor();
func(&p);
// here 'p' will actually be NULL
The only problem is if this function is in a different DLL (Windows). Then, it may be linked with a different version of the standard library and have different ideas on how the heap is built.
Otherwise no problem.
Passing p to func() by value, which will copy the pointer and creates the local copy to func() which frees the memory. func() then sets it's own instance of the pointer p to NULL but which is useless. Once the function is complete the parameter p come to end of existence. In calling function you still have pointer p holding an address, but the block is now on the free list and not useful for storage until allocated again.
What everybody is saying is that your memory will be freed by free(p);, but your original pointer (which you use to call the function with) will still hold the (now invalid) address. If a new block of memory including your address is allocated at a later stage than your original pointer will become valid (for memory manager) again, but will now point to completely different data causing all sorts of problems and confusion.
No you really free the block of memory. After the function call, the pointer passed to this function is pointing to nowhere : same address but the MMU don't know anymore what to do with this address

The state of a pointer after deallocation

What does the pointer of a dynamically allocated memory points to after calling the free() function.
Does the pointer points to NULL, or it still points to the same place it pointed before the deallocation.
does the implementation of free() has some kind of standard for this, or it's implemented differently in different platforms.
uint8_t * pointer = malloc(12);
printf("%p", pointer); // The current address the pointer points to
free (pointer);
printf("%p", pointer); // must it be NULL or the same value as before ?
Edit:
I know that the printf will produce the same results, I just want to know if I can count on that on different implementations.
The pointer value is not modified. You pass the pointer (memory address) by value to free(). The free() function does not have access to the pointer variable, so it cannot set it to NULL.
The two printf() calls should produce identical output.
According to the standard (6.2.4/2 of C99):
The value of a pointer becomes indeterminate when the object it points
to reaches the end of its lifetime.
In practice, all implementations I know of will print the same value twice for your example code. However, it is permitted that when you free the memory, the pointer value itself becomes a trap representation, and the implementation does something strange when you try to use the value of the pointer (even though you don't dereference it).
Supposing that an implementation wants to do something unusual, a hardware exception or program abort would be the most plausible I think. You probably have to imagine an implementation/hardware that does a lot of extra work, though, so that every time a pointer value is loaded into a register, it somehow checks whether the address is valid. That could be by checking it in the memory map (in which case I suppose my hypothetical implementation would only trap if the whole page containing the allocation has been released and unmapped), or some other means.
free() only deallocates the memory. The pointer is still pointing to the old location (dangling pointer), which you should manually set to NULL.
Setting the pointer to NULL is a good practice. As the memory location may be reused for other object, you may be able to access and modify data which doesn't belong to you. This is especially hard to debug, since it won't produce a crash, or produce crash at some point which is irrelevant. Setting to NULL will guarantee a re-producible crash if you ever access the non-existent object.
I tried this code in C++
#include<iostream>
using namespace std;
int main(void)
{
int *a=new int;
*a=5;
cout<<*a<<" "<<a<<"\n";
delete a;
*a=45;
cout<<*a<<" "<<a<<"\n";
}
The output was something like this
5 0x1066c20
45 0x1066c20
Running once more yielded a similar result
5 0x13e3c20
45 0x13e3c20
Hence it seems that in gcc the pointer still points to the same memory location after deallocation.
Moreover we can modify the value at that location.

Resources