Search code examples
cpointersundefined-behaviorfree

C calling free on not allocated memory


Why this program doesn't crash, is it undefined behaviour ?

int main()
{
   char* c;
   if (c) {
        printf("called free\n");
        free (c);
   }
   else {
        printf("not called free\n");
   }

   printf("not crashed\n");
   c = strdup("someString");

   if (c) {
        printf("called free\n"); 
        free(c);
        c = NULL;  // why is this needed for last if (c) 
   }
   
   if (c) {
        free(c);
   }
   printf("still not crashed\n");

    return 0;
}
  1. Is any possibility that this program will ever crash ?
  2. Should I always make such a if (c) verification before I call free on char* ?
  3. why without c=NULL free is called ?

Solution

  • First, some background: there are three kinds of pointer values:

    1. null pointers: either because you explicitly initialized to NULL, or because it was a global (or static) variable which is automatically initialized to a null pointer.
    2. valid pointers: a pointer that you have explicitly set to point to something: either by using &, or by calling malloc or realloc or strdup, and as long as the pointer has not become invalid.
    3. invalid pointers: pointers that are local ("automatic") variables that have not been initialized; pointers that were set (using &) to point to local variables in functions ("stack frames") that are no longer active; pointers that were once allocated using malloc or the others, but that have since been deallocated using free, or reallocated using realloc.

    You can tell if a pointer is in category 1 using an ordinary comparison: if(p == NULL), if(p != NULL).

    But the key point is that there is no programmatic way of distinguishing between pointer values in categories 2 and 3. That is, there is no way to write if(valid(p)) or if(invalid(p)).

    So the only way to avoid using invalid pointers is to keep track, yourself, somehow, of which pointers are valid and which are not. One strategy for doing this, pretty widely recommended as a good habit, is to set pointer variables to NULL whenever you know they're invalid.

    See also this answer and this answer.

    Now, for some specific answers to your code and questions:

    int main()
    {
    char* c;

    So c is a local variable. Since it is not initialized, it is unquestionably invalid (that is, category 3).

    if (c) {
    printf("called free\n");
    free (c);
    }
    else {
    printf("not called free\n");
    }

    This is a strange piece of code. It seems to acknowledge the possibility that the uninitialized pointer c might reliably start out as a null pointer, which as I just said is not the case. Also, this code is actually arranged, if anything, to maximize the possibility of a crash. If, by chance, c starts out as a null pointer, free is not called — but it turns out that free is guaranteed to behave gracefully — by quietly doing nothing — if it's passed a null pointer. Only if c is non-null — meaning that it's guaranteed to be invalid — is it handed to free, where it's virtually guaranteed to cause trouble.

    c = NULL; // why is this needed for last if (c)

    As mentioned, it's a good habit to set pointers to NULL after freeing them. A null pointer is testably invalid, while a non-NULL but freed pointer is undetectably invalid.

    1. Is any possibility that this program will ever crash ?

    Yes, it is quite likely to crash, since if tries to free invalid, uninitialized pointer, and (without that "why is this needed?" line), it frees a pointer twice.

    If you ran this code, and it didn't crash, that's kind of interesting, but it doesn't prove anything. Code that exhibits undefined behavior, such as trying to use the value of an uninitialized or otherwise invalid pointer, is not guaranteed to work. But it is not guaranteed not to work. "Undefined behavior" means that anything can happen, including code that appears to magically work. Undefined behavior does not mean that code is guaranteed to crash, or anything like that.

    1. Should I always make such a if (c) verification before I call free on char* ?

    You can, but I don't think it does what you think it does.

    It's not necessary, because as I mentioned, if you have a pointer that's already null, it's harmless to call free on it. So the test, in and of itself, doesn't protect you from anything.

    If you thought that the if(c) test did protect you from something, like by making sure you don't accidentally free an invalid pointer, it's not necessarily any good for that at all — unless you're religious about always setting pointers to NULL when you know they've become invalid.