clinuxgcc

How to fix "pointer ‘old’ may be used after ‘realloc’ [-Wuse-after-free]" warning in C?


I'm encountering a warning in my C code while trying to reallocate memory using realloc. The specific warning is:

lib/gc.c: In function ‘gcLocalRealloc’:
lib/gc.c:625:9: warning: pointer ‘old’ may be used after ‘realloc’ [-Wuse-after-free]
  625 |         gcLocalPop ( gc,old ) ;
      |         ^~~~~~~~~~~~~~~~~~~~~
lib/gc.c:621:11: note: call to ‘realloc’ here
  621 |     ptr = (void*) realloc ( ptr,SIZE ) ;

Here is the code that's causing the issue:

void* gcLocalRealloc(gc_t *gc, void* ptr, size_t SIZE) {
    //assert(SIZE!=0); // realloc(array, 0) is not equivalent to free(array).
    if (SIZE == 0) {
        gcFree(ptr);
        return ptr = NULL;
    }
    
    assert(SIZE > 0);
    
    if (ptr == NULL) return gcLocalMalloc(gc, SIZE);

    void* old = ptr;
    
    ptr = (void*) realloc(ptr, SIZE);

    if (ptr != NULL) {
        gcLocalPop(gc, old);
        gcLocalPush(gc, ptr, SIZE);
        assert(ptr != NULL);
    }

    return ptr;
}

The compiler warns that the pointer old may be used after it is freed by realloc. How can I fix this warning while maintaining the functionality of reallocating memory and updating my garbage collection?

Thanks in advance for your help!


Solution

  • Once you call realloc, if it returns a non-null value, then the value of old (which contains the prior pointer value) becomes indeterminate. As such, attempting to use this value (even if you don't dereference it) can cause undefined behavior.

    What you should do in this case is always call gcLocalPop before calling realloc, and just call gcLocalPush whether successful or not.

    void* gcLocalRealloc(gc_t *gc, void* ptr, size_t SIZE) {
        if (SIZE == 0) {
            gcFree(ptr);
            return ptr = NULL;
        }
        
        assert(SIZE > 0);
        if (ptr == NULL) return gcLocalMalloc(gc, SIZE);
    
        int old_size = gcLocalPop(gc, ptr);
        void* old = ptr;
        
        ptr = realloc(ptr, SIZE);
    
        if (ptr != NULL) {
            gcLocalPush(gc, ptr, SIZE);
            return ptr;
        } else {
            gcLocalPush(gc, old, old_size);
            return old;
        }
    }
    

    Note that you'll have to modify gcLocalPop to return the size of the removed item in case you need to put it back in the case of a realloc failure.