$ valgrind -q ./bin/06_use_after_free
what it printed on the machine that wrote this lab
==58996== Invalid read of size 4 (heap addrs representative)
==58996== at 0x4004C4: main (06_use_after_free.c:86)
==58996== Address 0x4a6a040 is 0 bytes inside a block of size 4 free'd
==58996== at 0x4843E43: free (vg_replace_malloc.c:990)
==58996== by 0x4004BF: main (06_use_after_free.c:85)
==58996== Block was alloc'd at
==58996== at 0x4840B26: malloc (vg_replace_malloc.c:447)
==58996== by 0x4004A8: main (06_use_after_free.c:83)
How to read it — valgrind gives you the FULL life story of the block:
"Invalid read of size 4 ... inside a block of size 4 free'd" -> you read
a 4-byte int from memory that was freed.
"free'd at ... main (...:85)" -> WHERE it was freed.
"Block was alloc'd at ... (...:83)" -> WHERE it was born.
Those three lines (use 86, free 85, alloc 83) are the whole bug.
$ clang -g -O0 -fsanitize=address 06_use_after_free.c -o bin/06_use_after_free.asan
$ ./bin/06_use_after_free.asan
what it printed on the machine that wrote this lab
==59001==ERROR: AddressSanitizer: heap-use-after-free on address 0x7b7b083e0010
READ of size 4 at 0x7b7b083e0010 thread T0
#0 0x4e9ed9 in main .../06_use_after_free.c:86:13
0x7b7b083e0010 is located 0 bytes inside of 4-byte region [0x...0010,0x...0014)
freed by thread T0 here:
#1 0x4e9ea4 in main .../06_use_after_free.c:85:5
previously allocated by thread T0 here:
#1 0x4e9e58 in main .../06_use_after_free.c:83:14
Same story as valgrind: a freed-then-read block. ASan keeps a "quarantine"
of recently-freed memory so the use-after-free is caught reliably instead
of silently reusing the bytes.
ROOT CAUSE vs CRASH SITE:
The read (line 86) is where it's observed, but the root cause is that the
pointer outlived its allocation: it was freed at line 85 yet still used.
The fix is about LIFETIME, not the read.