views:

259

answers:

8

I read somewhere that it is disastrous to use free to get rid of an object not created by calling malloc, is this true? why?

+4  A: 

It is undefined behaviour. And logically, if behaviour is undefined, you cannot be sure what has happened, and if the program is still operating properly.

Draco Ater
+1, but I can haz nasal demons?
Chris Lutz
but, how and why does it lead to an undefined behavior?
ultrajohn
Why? Because the standard says it does. How? I don't know. If we knew how it happened, it wouldn't be undefined.
Chris Lutz
@Chris: Why does "undefined behavior" always involve nasal demons, but not something nice, say, world peace?
peterchen
@peterchen - Because if undefined behavior involved world peace, I'd be all for it.
Chris Lutz
@peterchen: The Standard never says that UB can't be world peace. It can be world peace or whatever else.
sharptooth
A: 

At worst, it would simply result in undefined behavior, which could mean any number of things. The real disasters happen when you try to use unallocated memory, not when you try to free it.

kurige
Calling free on unallocated memory can cause problems depending on how free is implemented. It could therefore be a "real" disaster.
Dietrich Epp
Undefined behavior **is** a real disaster. Literally anything could happen to your program including data corruption, security vulnerabilities, crashing the program, etc. Any of those could happen (in theory and practice) just by freeing non-malloced memory.
Matthew Crumley
Aaaand that is why it is called 'undefined behaviour'.But. Taking into account differences between dozens of compilers and just as many platforms you can often call free on an unallocated block of memory with no ill side-effects. Not that that's advised. That's just not the case when you **use** unallocated pointers. By far the most prevalent problem in C code is the misuse of unallocated pointers.But thanks for the tip.
kurige
I disagree. Every time I've freed a pointer that wasn't dynamically allocated, it's caused some kind of corruption or crash. It's not necessarily when you call `free` though. I've had programs crash later on in the program when I tried to allocate memory. Reading or writing unallocated memory is probably more common, but I don't think it's any more or less disastrous.
Matthew Crumley
+6  A: 

That's undefined behavior - never try it.

Let's see what happens when you try to free() an automatic variable. The heap manager will have to deduce how to take ownership of the memory block. To do so it will either have to use some separate structure that lists all allocated blocks and that is very slow an rarely used or hope that the necessary data is located near the beginning of the block.

The latter is used quite often and here's how i is supposed to work. When you call malloc() the heap manager allocates a slightly bigger block, stores service data at the beginning and returns an offset pointer. Smth like:

void* malloc( size_t size )
{
      void* block = tryAlloc( size + sizeof( size_t) );
      if( block == 0 ) {
          return 0;
      }
      // the following is for illustration, more service data is usually written
      *((size_t*)block) = size;
      return (size_t*)block + 1;
 }

then free() will try to access that data by offsetting the passed pointer but if the pointer is to an automatic variable whatever data will be located where it expects to find service data. Hence undefined behavior. Many times service data is modified by free() for heap manager to take ownership of the block - so if the pointer passed is to an automatic variable some unrelated memory will be modified and read from.

Implementations may vary but you should never make any specific assumptions. Only call free() on addresses returned by malloc() family functions.

sharptooth
more please...:)
ultrajohn
You'd have to `tryAlloc(size + sizeof(size_t))` to have room for the `size_t` in front.
Chris Lutz
@Chris Lutz: Thank you, fixed that.
sharptooth
Usually there is a scheme followed to allocate memory (and more is allocated to store size, prevent seg-fault to *some* extent, etc). http://stackoverflow.com/questions/2650895/if-free-knows-the-length-of-my-array-why-cant-i-ask-for-it-in-my-own-code/2652417#2652417
N 1.1
Fix #2: You cast `block` to the right type, but forget to dereference it afterwards.
Chris Lutz
@Chris Lutz: True. Thank you.
sharptooth
I think you meant "never try it _unless_ your implementation defines the behavior" ... e.g. writing your own logging / garbage collecting malloc() subsystem. You are free to define any undefined behavior, that's why its undefined. (as long as you don't _break_ defined behavior).
Tim Post
@Tim Post: Then the code will expose undefined behavior on any other implementation. What will you do then?
sharptooth
@sharptooth - I didn't mean it was a good idea, I was simply pointing out undefined can be implementation defined. For instance, an embedded router .. a kid's toy .. a mobile device .. or anywhere else that you might fiddle with malloc() for whatever reason.
Tim Post
@sharptooth - also, implementation defined doesn't always mean _host_ implementation defined. For instance, if using uclibc and shipping it as part of the build.
Tim Post
+4  A: 
peterchen
+2  A: 

Some people have pointed out here that this is "undefined behavior". I'm going to go farther and say that on some implementations, this will either crash your program or cause data corruption. It has to do with how "malloc" and "free" are implemented.

One possible way to implement malloc/free is to put a small header before each allocated region. On a malloc'd region, that header would contain the size of the region. When the region is freed, that header is checked and the region is added to the appropriate freelist. If this happens to you, this is bad news. For example, if you free an object allocated on the stack, suddenly part of the stack is in the freelist. Then malloc might return that region in response to a future call, and you'll scribble data all over your stack. Another possibility is that you free a string constant. If that string constant is in read-only memory (it often is), this hypothetical implementation would cause a segfault and crash either after a later malloc or when free adds the object to its freelist.

This is a hypothetical implementation I am talking about, but you can use your imagination to see how it could go very, very wrong. Some implementations are very robust and are not vulnerable to this precise type of user error. Some implementations even allow you to set environment variables to diagnose these types of errors. Valgrind and other tools will also detect these errors.

Dietrich Epp
undefined behavior as officially defined - `behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements`. Since standard imposes no requirements, there are bound to be different implementations and the program _may_ crash on some implementations.
N 1.1
A: 

It would certainly be possible for an implementation of malloc/free to keep a list of the memory blocks thats been allocated and in the case the user tries to free a block that isn't in this list do nothing.

However since the standard says that this isn't a requirement most implementation will treat all pointers coming into free as valid.

Andreas Brinck
A: 

Please have a look at what undefined behavior means. malloc() and free() on a conforming hosted C implementation are built to standards. The standards say the behavior of calling free() on a heap block that was not returned by malloc() (or something wrapping it, e.g. calloc()) is undefined.

This means, it can do whatever you want it to do, provided that you make the necessary modifications to free() on your own. You won't break the standard by making the behavior of free() on blocks not allocated by malloc() consistent and even possibly useful.

In fact, there could be platforms that (themselves) define this behavior. I don't know of any, but there could be some. There are several garbage collecting / logging malloc() implementations that might let it fail more gracefully while logging the event. But thats implementation , not standards defined behavior.

Undefined simply means don't count on any kind of consistent behavior unless you implement it yourself without breaking any defined behavior. Finally, implementation defined does not always mean defined by the host system. Many programs link against (and ship) uclibc. In that case, the implementation is self contained, consistent and portable.

Tim Post
+2  A: 

Strictly speaking, this is not true. calloc() and realloc() are valid object sources for free(), too. ;)

Secure
Strictly speaking, you are correct. However, calloc() and realloc() are just convenient interfaces to malloc(). If given a platform with only malloc() and memcpy(), one could quickly implement calloc() and realloc() in just a few minutes.
Tim Post
Not exactly. To copy the data for realloc, you need to know the size of the old memory block.
Secure