FForge
← Labsdebug-15Runtime debugging●●○○

free() of a non-heap pointer

debug/15_invalid_free.c3 steps
0/3 steps
evidence to leave with
glibc invalid pointer abort; ASan attempting free on address not malloc()-ed
walk it, one step at a time
run this
$ clang -g -O0 15_invalid_free.c -o bin/15_invalid_free
$ ./bin/15_invalid_free ; echo "exit=$?"
what it printed on the machine that wrote this lab
free(): invalid pointer
Aborted (core dumped)
exit=134
run this
$ gdb -q -batch -ex 'set debuginfod enabled off' -ex run -ex bt \
what it printed on the machine that wrote this lab
      ./bin/15_invalid_free
free(): invalid pointer
Program received signal SIGABRT, Aborted.
...
#6  0x0000000000400497 in main () at 15_invalid_free.c:73
How to read it: bottom-up, frame #6 is YOUR code — main at line 73, the
bad free(). The frames above are glibc's abort path (free -> malloc_printerr
-> abort -> raise), same shape as the double-free (#07). gdb hands you the
exact offending free.
run this
$ valgrind -q ./bin/15_invalid_free
what it printed on the machine that wrote this lab
  ==NNNNN== Invalid free() / delete / delete[] / realloc()
  ==NNNNN==    at 0x........: free (vg_replace_malloc.c:...)
  ==NNNNN==    by 0x........: main (15_invalid_free.c:73)
  ==NNNNN==  Address 0x........ is on thread 1's stack
  ==NNNNN==  in frame #1, created by main (15_invalid_free.c:70)
  How to read it — valgrind is the clearest of all here:
    "Invalid free()" at main (...:73)        -> the bad free call.
    "Address ... is on thread 1's stack"        -> it spells out that you
       freed a STACK address, not a heap block. That one line names the bug.
run this
$ clang -g -O0 -fsanitize=address 15_invalid_free.c -o bin/15_invalid_free.asan
$ ./bin/15_invalid_free.asan
what it printed on the machine that wrote this lab
  ==NNNNN==ERROR: AddressSanitizer: attempting free on address which was not
      malloc()-ed: 0x.... in thread T0
      #0 0x... in free
      #1 0x... in main .../15_invalid_free.c:73
  ASan states it literally: "free on address which was not malloc()-ed".

ROOT CAUSE vs CRASH SITE: The crash is inside glibc's free, but the bug is the free() of a pointer that never came from malloc (line 73). Root cause: confusing automatic (stack) storage with heap ownership. Only free() what malloc() gave you.

category

heap error

difficulty

2 ------------------------------------------------------------

what's wrong

`p` points at a STACK variable (`&x`), then `free(p)` is called. free() may only be given a pointer returned by malloc/calloc/realloc (or NULL). Passing a stack address corrupts the allocator. Buggy line: `free(p);`.

symptom

glibc DETECTS it: "free(): invalid pointer", SIGABRT, exit 134. Deterministic on glibc.

the fix

Don't free stack/global/interior pointers. Free exactly the pointers malloc returned. Here: just delete the `free(p);` — `x` is automatic and is reclaimed when main returns.

take it further

Make `p` point one past a real heap block: `char*h=malloc(8); free(h+1);`. Now valgrind/ASan say "not 0 bytes inside" / freeing an interior pointer — another flavor of invalid free. ============================================================

read the source · debug/15_invalid_free.c
#include <stdlib.h> int main(void) { int x = 42; int *p = &x; /* p points at a STACK variable */ free(p); /* <-- THE BUG: free() of a non-heap ptr */ return 0; }
◇
Finish the lab: write the evidence note

A lab is done when you can say what it showed on your machine — not when you have read it.