5

How come I don't get any error using the following program?

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
  char *pStr = (char*) malloc(25); 
  free(pStr); 
  strcpy(pStr, "foo");
  printf("%s\n", pStr);
  return 0;
}

Shouldn't free(pStr) stop me from writing to that address? Don't I have to allocate the space again before I can use it?

Oskar Persson
  • 6,605
  • 15
  • 63
  • 124
  • 3
    Where, in any contract, documentation, example code, etc, does it say that the C language prevents you from writing to memory?? If it doesn't say that anywhere, where did you get the idea from? – abelenky Dec 02 '14 at 20:16
  • 2
    "Undefined" behavior means exactly that: it might segfault, it might crash, it might make demons fly out of your nose,...or it might work perfectly, never giving you any clue that it's broken. – Lee Daniel Crocker Dec 02 '14 at 20:17
  • 1
    The C programming language is one step above assembly language. It is the lowest of the high level programming languages. As such it allows you to do whatever you want, and assumes that you know what you're doing. There is no safety net for newbies. – user3386109 Dec 02 '14 at 20:18
  • 2
    I would call it the highest of the low-level languages, but yes, that's the basic idea. – Lee Daniel Crocker Dec 02 '14 at 20:19
  • Dennis Ritchie famously said: ***"C has all the power of Assembly, with all the convenience of Assembly"*** – abelenky Dec 02 '14 at 20:28

4 Answers4

11

free doesn't prevent you from doing anything as long as the thing is syntactically correct. So you are still more than welcome to copy to a char* just as you could do if you had never allocated the memory in the first place. But this is undefined behavior, and is liable (read: likely) to cause your program to crash or do something wrong without warning.

For example, if your compiler does some optimizations, it might reorder some instructions in order to save time. Since you have freed the memory, the compiler might think that it is safe to allocate memory in that location for some other data that will be created later. If the optimizer moves that allocation and write to before your strcpy here, you could overwrite the object that is stored there.

Consider this code:

int main(int arc, char *argv[]){
    char* pStr = (char*) malloc(25);
    free(pStr);
    strcpy(pStr, "foo");
    printf("%s\n", pStr);
    int* a = (int*) malloc(sizeof(int));
    *a = 36;
    printf("%d\n", *a);
}

Since you wrote to unallocated memory, you can't be sure what either of the printfs will display. The 36 might possibly have overwritten some of the "foo" string. The "foo" string might have overwritten the value 36 that a points to. Or maybe neither of them affects the other and your program runs seemingly just fine until you change the name of some variable and recompile and for some reason everything is messed up even though you didn't change anything.

Moral of the story: you are correct that you should not write to freed memory; however, you are incorrect that you cannot write to freed memory. C does not check this condition and assumes that you are trying to do something fancy. If you know exactly how your compiler optimizes and where it allocates memory when malloc is called, you might know that writing to unallocated memory is safe in a particular case, and C does not prevent you from doing that. But for the 99.999% of the time that you don't know everything, just don't do it.

Daniel
  • 6,595
  • 9
  • 38
  • 70
5

It is an undefined behavior. And what really happens is implementation specific.

In practice, free very often mark the freed memory block as reusable for future malloc-s.

See also this ...

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
4

As other answers pointed it's undefined behavior, thus compiler is not obligated for any diagnostics. However if you have modern version of gcc (>= 4.8) or clang, then AddressSanitizer might be helpful in case of this "use-after-free" bug:

$ gcc -ansi -pedantic -fsanitize=address check.c
$ ./a.out 
=================================================================
==2193== ERROR: AddressSanitizer: heap-use-after-free on address 0x60060000efe0 at pc 0x4009a8 bp 0x7fff62e22bc0 sp 0x7fff62e22bb8
...

The common defensive programming "trick" is to assign NULL right after free() call:

free(pStr), pStr = NULL;

With it It's likely to get "Segmentation fault" with pStr dereference on GNU/Linux, but there is no guarantee on that.

Grzegorz Szpetkowski
  • 36,988
  • 6
  • 90
  • 137
0

Understand the answer to your question you need to understand the process of memory allocation. In a generic sense, malloc/free are library functions. They manage a memory pool that is allocated from operating system services.

[at the risk of oversimplification]

Your first malloc finds an empty pool. It then calls an operating system system service to add pages to the process that get added to the pool. Malloc then returns a block of memory from that pool.

Calling free returns the block to the pool. It remains a valid block of memory in the pool.

If you access the free'd address, the memory is still there. However, you are #$@$ing up malloc's pool of memory. That kind of access is going to eventually byte you in the #$#$.

user3344003
  • 20,574
  • 3
  • 26
  • 62