This question already has answers here:
C - Accessing data AFTER memory has been free()ed?
(2 answers)
Closed 2 years ago.
I am new to C. I have below codes. After free, the first freed pointer give 0 length but not null, the second and third still have length > 0. Not sure if it is normal? Thanks.
Here is the output:
enter image description here
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *str[3];
char *aa = "helloworld0";
char *bb = "helloworld1";
char *cc = "helloworld2";
str[0] = (char *)malloc(strlen(aa) + 1);
strcpy(str[0], aa);
str[1] = (char *)malloc(strlen(bb) + 1);
strcpy(str[1], bb);
str[2] = (char *)malloc(strlen(cc) + 1);
strcpy(str[2], cc);
char **strr = str;
printf("Before free----Length:%ld,Content:%s,Address:%p\n",
strlen(strr[1]), strr[1], strr[1]);
free(strr[1]);
if (strr[1]) printf("not NULL 1\n");
printf("After free----Length:%ld,Content:%s,Address:%p\n\n",
strlen(strr[1]), strr[1], strr[1]);
printf("Before free----Length:%ld,Content:%s,Address:%p\n",
strlen(strr[0]), strr[0], strr[0]);
free(strr[0]);
if (strr[0] == NULL) printf("not NULL 0\n");
printf("After free----Length:%ld,Content:%s,Address:%p\n\n",
strlen(strr[0]), strr[0], strr[0]);
printf("Before free----Length:%ld,Content:%s,Address:%p\n",
strlen(strr[2]), strr[2], strr[2]);
free(strr[2]);
if (strr[2]) printf("not NULL 2\n");
printf("After free----Length:%ld,Content:%s,Address:%p\n\n",
strlen(strr[2]), strr[2], strr[2]);
}
After passing a pointer value to free, any further use of that value has undefined behavior. Even if (strr[1]) is invalid and does not have a meaningful result, even if in practice it normally reflects that the last time the value was meaningful, it was non-null.
The subsequent operation is even worse:
printf("After free----Length:%ld,Content:%s,Address:%p\n\n",strlen(strr[1]),strr[1],strr[1]);
Here you're not only using the value strr[1] (a pointer), but passing a function that will use it to access an object at that address (strlen). This also has undefined behavior, and mechanically it ends up accessing memory that no longer belongs to your program. What this results in is a matter of how the compiler implementation handles undefined behavior it can see (note: it could even trap before letting you get this far, but it doesn't have to) and possibly on what the C library implementation (free) does with memory you've relinquished to it. It's possible that the address will no longer work and that accesses to it will fault and cause your program to crash. It's also possible that it ends up accessing memory that now belongs to the implementation, possibly with new contents stored there to track it for reuse. It's also possible that entirely different and unexpected results are produced.
In C you simply can't do this. After you free memory obtained by malloc, you're done with it. You can't process the pointer or the memory it pointed to before in any way. There is no way to "check if it was freed". Once it's freed, that's it.
Many programmers like to assign a null pointer value to any pointer object as soon as they free the memory it points to, to prevent inadvertently trying to access it later:
strr[1] = NULL; // or = 0;
Free memory actually means freeing ownership of specified place in the memory. The idea is that in the process of freeing it the variable it self can't write anymore off the given location but only read. In order to actually make the code safe after every free you should set the variable to NULL. It will redirect the variable off the last memory's sell it was pointing to and make the data unreadable from the variable it self. Instead the pointer will hold a NULL which is pre known state of a pointer free of ownership and thus may be used.
free means you are telling system:
I do not need this memory anymore, you can use this memory anytime if you want.
But system does not guarantee when will it clean and re-use these memory.
Next time when you access this memory, maybe system has not cleaned it yet, or maybe it does. You just can not make sure whether it does. It is a undefined behavior.
You should add strr[1]=NULL; to prevent you accidentally access the space which you have already returned to system.
Related
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
char* test() {
char* s = "Hello World";
size_t len = strlen(s);
char* t = malloc(sizeof(char)*(len+1));
strcpy(t, s);
free(t);
return t;
};
int main(void) {
printf("%s\n", test());
return 0;
};
I would like to allocate and de-allocate memory inside the function. I tested this code and works, but I am wondering:
Why does this work?
Is it good practice to use the value of a freed pointer in main ?
Once you call free on a pointer, the memory it pointed to is no longer valid. Attempting to use a pointer to freed memory triggers undefined behavior. In this particular case it happened to work, but there's no guarantee of that.
If the function returns allocated memory, it is the responsibility of the caller to free it:
char* test() {
char* s = "Hello World";
size_t len = strlen(s);
char* t = malloc(sizeof(char)*(len+1));
strcpy(t, s);
return t;
};
int main(void) {
char *t = test();
printf("%s\n", t);
free(t);
return 0;
};
malloc reserves memory for use.
free releases that reservation. In general, it does not make the memory go away, it does not change the contents of that memory, and it does not alter the value of the pointer that held the address.
After free(t), the bytes of t still contain the same bit settings they did before the free. Then return t; returns those bits to the caller.
When main passes those bits to printf, printf uses them as the address to get the characters for %s. Since nothing has changed them, they are printed.
That is why you got the behavior you did with this program. However, none of it is guaranteed. Once free was called with t, the memory reservation was gone. Something else in your program could have used that memory. For example, printf might have allocated a buffer for its own internal use, and that could have used the same memory.
For the most part, malloc and free are just methods of coordinating use of memory, so that different parts of your program do not try to use the same memory at the same time for different purposes. When you only have one part of your program using allocated memory, there are no other parts of your program to interfere with that. So the lack of coordination did not cause your program to fail. If you had multiple routines in your program using allocated memory, then attempting to use memory after it has been released is more likely to encounter problems.
Additionally, once the memory has been freed, the compiler may treat a pointer to it as if it has no fixed value. The return t; statement is not required to return any particular value.
It doesn't matter where do you free() a pointer. Once it is free()d, the pointer is not deferrenciable anymore (neither inside nor ouside the function where it was free()d)
The purpose of free() is to return the memory allocated with malloc() so the semantics are that, once you have freed a chunk of memory, it is not anymore usable.
In C, all parameters are passed by value, so free() cannot change the value expression you passed to it, and this is the reason the pointer is not changed into an invalid pointer value (like NULL) but you are advised that no more uses of the pointer can be done without incurring in Undefined Behaviour.
There could be a solution in the design of free() and it is to pass the pointer variable that holds the pointer by address, and so free() would be able to turn the pointer into a NULL. But this not only takes more work to do, but free() doesn't know how many copies you have made of the value malloc() gave to you... so it is impossible to know how many references you have over there to be nullified. That approach makes it impossible to give free() the responsibility of nullifying the reference to the returned memory.
So, if you think that free doesn't turn the pointer into NULL and for some strange reason you can still use the memory returned, don't do it anymore, because you'll be making mistakes.
You are adviced! :)
This question already has answers here:
What's the point of malloc(0)?
(17 answers)
Closed 4 years ago.
I know that malloc(size_t size) allocates size bytes and returns a pointer to the allocated memory. .
So how come when I allocate zero bytes to the integer pointer p, I am still able to initialize it?
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *p = malloc(0);
*p = 10;
printf("Pointer address is: %p\n", p);
printf("The value of the pointer: %d\n", *p);
return 0;
}
Here is my program output, I was expecting a segmentation fault.
Pointer address is: 0x1ebd260
The value of the pointer: 10
The behavior of malloc(0) is implementation defined, it will either return a pointer or NULL. As per C Standard, you should not use the pointer returned by malloc when requested zero size space1).
Dereferencing the pointer returned by malloc(0) is undefined behavior which includes the program may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.
1) From C Standard#7.22.3p1 [emphasis added]:
1 The order and contiguity of storage allocated by successive calls to the aligned_alloc, calloc, malloc, and realloc functions is unspecified. The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated (until the space is explicitly deallocated). The lifetime of an allocated object extends from the allocation until the deallocation. Each such allocation shall yield a pointer to an object disjoint from any other object. The pointer returned points to the start (lowest byte address) of the allocated space. If the space cannot be allocated, a null pointer is returned. If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
When you call malloc(0) and write to the returned buffer, you invoke undefined behavior. That means you can't predict how the program will behave. It might crash, it might output strange results, or (as in this case) it may appear to work properly.
Just because the program could crash doesn't mean it will.
In general, C does not prevent you from doing incorrect things.
After int *p = malloc(0);, p has some value. It might be a null pointer, or it might point to one or more bytes of memory. In either case, you should not use it.1 But the C language does not stop you from doing so.
When you execute *p = 10;, the compiler may have generated code to write 10 to the place where p points. p may be pointing at actual writable memory, so the store instruction may execute without failing. And then you have written 10 to a place in memory where you should not. At this point, the C standard no longer specifies what the behavior of your program is—by writing to an inappropriate place, you have broken the model of how C works.
It is also possible your compiler recognizes that *p = 10; is incorrect code in this situation and generates something other than the write to memory described above. A good compiler might give you a warning message for this code, but the compiler is not obligated to do this by the C standard, and it can allow your program to break in other ways.
Footnote
1 If malloc returns a null pointer, you should not write to *p because it is not pointing to an object. If it returns something else, you should not write to *p because C 2018 7.22.3.1 says, for this of malloc(0), “the returned pointer shall not be used to access an object.”
I think it is good idea to look at segmentation fault definition https://en.wikipedia.org/wiki/Segmentation_fault
The following are some typical causes of a segmentation fault:
Attempting to access a nonexistent memory address (outside process's address space)
Attempting to access memory the program does not have rights to (such as kernel structures in process context)
Attempting to write read-only memory (such as code segment)
So in general, access to any address in program's data segment will not lead to seg fault. This approach makes sence as C doesn't have any framework with memory management in it (like C# or Java). So as malloc in this example returns some address (not NULL) it returns it from program's data segment which can be accessed by program.
However if program is complex, such action (*p = 10) may overwrite data belonging to some other variable or object (or even pointer!) and lead to undefined behaviour (including seg fault).
But please take in account, that as described in other answers such program is not best practice and you shouldn't use such approach in production.
This is a source code of malloc() (maybe have a litter difference between kernel versions but concept still like this), It can answer your question:
static void *malloc(int size)
{
void *p;
if (size < 0)
error("Malloc error");
if (!malloc_ptr)
malloc_ptr = free_mem_ptr;
malloc_ptr = (malloc_ptr + 3) & ~3; /* Align */
p = (void *)malloc_ptr;
malloc_ptr += size;
if (free_mem_end_ptr && malloc_ptr >= free_mem_end_ptr)
error("Out of memory");
malloc_count++;
return p;
}
When you assigned size is 0, it already gave you a pointer to first address of memory
When you free memory, what happens to pointers that point into that memory? Do they become invalid immediately? What happens if they later become valid again?
Certainly, the usual case of a pointer going invalid then becoming "valid" again would be some other object getting allocated into what happens to be the memory that was used before, and if you use the pointer to access memory, that's obviously undefined behavior. Dangling pointer memory overwrite lesson 1, pretty much.
But what if the memory becomes valid again for the same allocation? There's only one Standard way for that to happen: realloc(). If you have a pointer to somewhere within a malloc()'d memory block at offset > 1, then use realloc() to shrink the block to less than your offset, your pointer becomes invalid, obviously. If you then use realloc() again grow the block back to at least cover the object type pointed to by the dangling pointer, and in neither case did realloc() move the memory block, is the dangling pointer valid again?
This is such a corner case that I don't really know how to interpret the C or C++ standards to figure it out. The below is a program that shows it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
static const char s_message[] = "hello there";
static const char s_kitty[] = "kitty";
char *string = malloc(sizeof(s_message));
if (!string)
{
fprintf(stderr, "malloc failed\n");
return 1;
}
memcpy(string, s_message, sizeof(s_message));
printf("%p %s\n", string, string);
char *overwrite = string + 6;
*overwrite = '\0';
printf("%p %s\n", string, string);
string[4] = '\0';
char *new_string = realloc(string, 5);
if (new_string != string)
{
fprintf(stderr, "realloc #1 failed or moved the string\n");
free(new_string ? new_string : string);
return 1;
}
string = new_string;
printf("%p %s\n", string, string);
new_string = realloc(string, 6 + sizeof(s_kitty));
if (new_string != string)
{
fprintf(stderr, "realloc #2 failed or moved the string\n");
free(new_string ? new_string : string);
return 1;
}
// Is this defined behavior, even though at one point,
// "overwrite" was a dangling pointer?
memcpy(overwrite, s_kitty, sizeof(s_kitty));
string[4] = s_message[4];
printf("%p %s\n", string, string);
free(string);
return 0;
}
When you free memory, what happens to pointers that point into that memory? Do they become invalid immediately?
Yes, definitely. From section 6.2.4 of the C standard:
The lifetime of an object is the portion of program execution during which storage is
guaranteed to be reserved for it. An object exists, has a constant address, and retains
its last-stored value throughout its lifetime. 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.
And from section 7.22.3.5:
The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. The contents of the new
object shall be the same as that of the old object prior to deallocation, up to the lesser of
the new and old sizes. Any bytes in the new object beyond the size of the old object have
indeterminate values.
Note the reference to old object and new object ... by the standard, what you get back from realloc is a different object than what you had before; it's no different from doing a free and then a malloc, and there is no guarantee that the two objects have the same address, even if the new size is <= the old size ... and in real implementations they often won't because objects of different sizes are drawn from different free lists.
What happens if they later become valid again?
There's no such animal. Validity isn't some event that takes place, it's an abstract condition placed by the C standard. Your pointers might happen to work in some implementation, but all bets are off once you free the memory they point into.
But what if the memory becomes valid again for the same allocation? There's only one Standard way for that to happen: realloc()
Sorry, no, the C Standard does not contain any language to that effect.
If you then use realloc() again grow the block back to at least cover the object type pointed to by the dangling pointer, and in neither case did realloc() move the memory block
You can't know whether it will ... the standard does not guarantee any such thing. And notably, when you realloc to a smaller size, most implementations modify the memory immediately following the shortened block; reallocing back to the original size will have some garbage in the added part, it won't be what it was before it was shrunk. In some implementations, some block sizes are kept on lists for that block size; reallocating to a different size will give you totally different memory. And in a program with multiple threads, any freed memory can be allocated in a different thread between the two reallocs, in which case the realloc for a larger size will be forced to move the object to a different location.
is the dangling pointer valid again?
See above; invalid is invalid; there's no going back.
This is such a corner case that I don't really know how to interpret the C or C++ standards to figure it out.
It's not any sort of corner case and I don't know what you're seeing in the standard, which is quite clear that freed memory has indeteterminate content and that the values of any pointers to or into it are also indeterminate, and makes no claim that they are magically restored by a later realloc.
Note that modern optimizing compilers are written to know about undefined behavior and take advantage of it. As soon as you realloc string, overwrite is invalid, and the compiler is free to trash it ... e.g., it might be in a register that the compiler reallocates for temporaries or parameter passing. Whether any compiler does this, it can, precisely because the standard is quite clear about pointers into objects becoming invalid when the object's lifetime ends.
If you then use realloc() again grow the block back to at least cover the object type pointed to by the dangling pointer, and in neither case did realloc() move the memory block, is the dangling pointer valid again?
No. Unless realloc() returns a null pointer, the call terminates the lifetime of the allocated object, implying that all pointers pointing into it become invalid. If realloc() succeeds, it returns the address of a new object.
Of course, it just might happen that it's the same address as the old one. In that case, using an invalid pointer to the old object to access the new one will generally work in non-optimizing implementations of the C language.
It would still be undefined behaviour, though, and might actually fail with aggressively optimizing compilers.
The C language is unsound, and it's generally up to the programmer to uphold its invariants. Failing to do so will break the implicit contract with the compiler and may result in incorrect code being generated.
It depends on your definition of "valid". You've perfectly described the situation. If you want to consider that "valid", then it's valid. If you don't want to consider that "valid", then it's invalid.
I am now studying C and in some code examples I saw that after we allocate some memory to a pointer, we have to check the pointer to be not a NULL. For example:
CVector *vector = malloc(sizeof(struct CVectorImplementation));
assert(vector != NULL);
another example:
vector->elements = realloc(vector->elements, vector->elemsz * vector->vec_capacity);
assert(vector->elements != NULL);
However, I think since the pointer is already been allocated, then it has the address of the allocated memory as its value, thus is it always necessary? why?
If you've reassigned the original pointer in response to realloc, it's too late to do anything useful in response to a failure. When realloc fails, it returns NULL, but it does not free the original pointer. So even if you have some reasonable response to an allocation failure (not common), you've already leaked the memory you were trying to realloc.
The answer to your main question is mostly "it's a bad idea to allow NULL pointer dereferences to occur because it's a source of vulnerabilities"; usually the vulnerabilities crop up in kernel code (where NULL is just as valid an address as anywhere else), but even when it's not exploitable, it means the program segfaults instead of reporting an error in a useful way.
It's a great idea to check the pointer returned from malloc/realloc.
If there's an error, you will get a null value returned. Use this check to your advantage because if you make reference to the same pointer later in your program and your program suddenly crashes, then chances are the pointer is set to null.
If you do have a valid pointer from a malloc/realloc call, then make sure you use it inside the free() function before deciding to modify the pointer value and before the program terminates, otherwise, you may run into memory leaks.
If you need to change the pointer value to write to a different section of the memory you allocated, then use another pointer.
Here's code in C that shows what I mean:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char *block=calloc(1,10000);
if (block==NULL){
printf("Can't allocate memory\n");
return -1;
}
memset(block,48,20); //set 1st 20 bytes of memory to number zero (ascii code 48)
char *insideoftheblock=block+10; // I set a pointer to go to index #10 in the memory
*insideoftheblock='x';
*insideoftheblock++;
*insideoftheblock='y';
printf("Memory = '%s'",block);
free(block);
}
P.S.
I updated my code to include a check to see if memory has been actually allocated.
The realloc function attempts to allocate new memory. If this allocation fails then the realloc function returns NULL. Your code must deal with this situation.
If you want to abort your program in this case then the assert as you currently have it is suitable. If you want to recover, then you will need to store the realloc result in a separate variable while you assess the situation, e.g.:
void *new = realloc(vector->elements, vector->elemsz * vector->vec_capacity);
if ( !new )
// take some action.... the old vector->elements is still valid
else
vector->elements = new;
A failed allocation typical results in 1 of 2 actions:
1) Exit the program with a diagnostic. This is far better than not checking and letting the code continue to who--knows--what.
2) In select circumstances, code can cope with the failure. Maybe freeing other resources and trying again, return a failure code and leave the problem to the calling routine or writing a "suicide note" and re-starting the system. IAC, the action is very specific to the situation.
Robust code checks the result. Beginner code does not.
What happens to the data that is present in a memory location that has just been freed by a free() ? Is that data also deleted and the memory will now have a garbage value ? Or that data still persists there untill a new data is stored in that memory location (in future) ?
I mean, for code below:
int *ptr;
ptr = malloc(sizeof(int));
*ptr = 1;
// Suppose ptr = 2000
//Free now
free(ptr);
// My question is what is the value stored in memory address 2000 now ?
// Is it still '1' or some garbage value ?
The result is unpredictable. There are several options that can happen. The point is that you cannot rely on any behavior of the memory released by free()
Some examples:
the memory can be untouched (remain the same as it is with the same data).
It can be given to another memory allocation, in which case it can be written over at any point.
It can be zeroed.
The page containing the memory can be returned to the OS, removing it from the memory map of your process, making your program crash if you try to access it.
Whether or not the value is overwritten is undefined. Once free is called it is allowed to leave the memory as-is or it can overwrite it, but if you are interested in security you should overwrite it yourself before deallocating it. Speaking of deallocation, free doesn't have to give the memory back to the operating system, and in fact in many cases it won't, instead it will keep the memory allocated to your program so that the next time you call malloc it can simply give you back the same memory and avoid having to make more system calls, since the time it takes for memory allocation from the operating system is generally considered a less efficient use of resources than the program keeping a bit more memory allocated than it needs.
I know that using the C free() function the memory used is released, but neither the pointer, nor the value contained in the memory is modified! free() only tells that the memory may be used for other purposes. (It may be some libraries implementations clean the freed memory or the pointer value, but this should not be the standard!)
I tried the code below with gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
int main(void)
{
int *i,j;
i=malloc(100*sizeof(int));
for(j=0;j<100;j++)
i[j]=j+1;
printf("%p %d\n",i,i[0]);
free(i);
printf("%p %d\n",i,i[0]);
return 0;
}
The output results (as I expected) is:
0x1de2010 1
0x1de2010 1
Malloc() is a library function. The answer depends upon how the library is implemented.
Most (if not all) mallocs prefix a header to the memory block returned. This is usually modified.
Some mallocs append a trailer to the memory block and write something to it. This is used to detect buffer overruns.
Some frees() will overwrite the write the returned memory with some bit pattern to detect subsequent writes.
There are a lot of mallocs out there that you can download and link with your application so you can get nearly any behavior you want by linking the malloc you want with your application.
It depends on the compiler. If you are using gcc then after free value of that memory is become 0.
Here is a sample code:
#include<stdio.h>
#include<stdlib.h>
int main ( void )
{
int *ptr = NULL;
ptr = malloc (sizeof(int));
*ptr = 5;
printf ( "\n value of *ptr = %d", *ptr );
free ( ptr );
printf ( "\n value of *ptr = %d", *ptr );
return ( 0 );
}
o/p:
./a.out
value of *ptr = 5
value of *ptr = 0
./a.out
value of *ptr = 5
value of *ptr = 0
./a.out
value of *ptr = 5
value of *ptr = 0
Dereferencing a freed pointer leads to undefined behavior, which means anything is allowed to happen.
Most likely, you'll get some garbage value, but you might also trigger a segmentation fault, which will crash your program. Even so, neither of those behaviors are guaranteed, and you shouldn't rely on them.