$ valgrind -q ./bin/04_stack_oob_write ; echo "exit=$?"
what it printed on the machine that wrote this lab
buf[0]=0 (looks fine?!)
exit=0
^ valgrind/memcheck reports NOTHING. memcheck has no red zones around
STACK arrays, so an overflow that stays within the mapped stack page is
invisible to it. META-LESSON: "valgrind clean" != "no bug".
$ clang -g -O0 -fsanitize=address 04_stack_oob_write.c -o bin/04_stack_oob_write.asan
$ ./bin/04_stack_oob_write.asan ; echo "exit=$?"
what it printed on the machine that wrote this lab
==NNNNN==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x.... at pc 0x....
WRITE of size 4 at 0x.... thread T0
#0 0x... in main .../04_stack_oob_write.c:111:16
Address 0x.... is located in stack of thread T0 at offset 48 in frame
#0 0x... in main .../04_stack_oob_write.c:108
This frame has 1 object(s):
[32, 48) 'buf' (line 109) <== Memory access at offset 48 overflows this variable
... Shadow bytes around the buggy address: ...
=>0x............: f1 f1 f1 f1 00 00[f3]f3 00 00 ...
exit=1
How to read ASan:
"stack-buffer-overflow" + "WRITE of size 4" -> a 4-byte (int) write past
an array on the stack, at .../04_stack_oob_write.c:111 column 16.
"[32,48) 'buf' ... offset 48 overflows" -> buf occupies bytes 32..47 of
the frame (16 bytes = 4 ints); the access at offset 48 is one int too
far. ASan names the VARIABLE and the exact write — far more precise
than the canary or gdb.
Shadow legend: f1 = left redzone, 00 = addressable (buf's 16 bytes),
f3 = right redzone (poisoned guard). The [..] marks the byte hit.
ASan surrounds every stack array with poisoned redzones; touching one
is an instant, pinpointed error — which is why ASan catches what
valgrind misses here, and ~10x faster too.
(ASan addresses are ASLR-randomized: representative — yours will differ.)
ROOT CAUSE vs CRASH SITE:
Three different tools point at three different lines, and understanding why
IS the lesson:
- Plain build: no crash at all (corruption is silent).
- Canary build / gdb: aborts at the RETURN (line 113) — that's when the
smashed canary is *checked*, long after the damage.
- ASan: the WRITE itself (line 111) — the true root cause.
The corruption happens at line 111; everything downstream is a delayed
symptom. Always prefer the tool that flags the WRITE, not the aftermath.