$ clang -g -O0 05_heap_overflow.c -o bin/05_heap_overflow
$ ./bin/05_heap_overflow ; echo "exit=$?"
stored, p[7]=k
exit=0
^ Silent again — the plain build gives no hint p[8] was out of bounds.$ clang -g -O0 05_heap_overflow.c -o bin/05_heap_overflow
$ ./bin/05_heap_overflow ; echo "exit=$?"
stored, p[7]=k
exit=0
^ Silent again — the plain build gives no hint p[8] was out of bounds.Because the plain run doesn't fault, `gdb run` just exits 0. gdb is for the CRASH; for a silent heap overflow you either (a) reach for valgrind/ ASan (STEP 3), or (b) set a hardware WATCHPOINT on the byte after the block to catch the write. Demo of a watchpoint:
$ gdb -q -batch -ex 'set debuginfod enabled off' \
-ex 'break 90' -ex run -ex 'print (void*)&p[8]' \
-ex 'watch *(char*)(p+8)' -ex continue -ex bt ./bin/05_heap_overflow
Breakpoint 1, main () at 05_heap_overflow.c:90
90 p[8] = '!'; // <-- THE BUG
$1 = (void *) 0x404318 # addr of the OOB byte (representative)
Hardware watchpoint 2: *(char*)(p+8)
Old value = 0 '\000'
New value = 33 '!' # 33 == '!': the OOB write just happened
main () at 05_heap_overflow.c:91
#0 main () at 05_heap_overflow.c:91
(watch *ADDR -> gdb stops the instant that memory changes; it reports the
NEXT line 91 after the store, so look one line up for the writer.)$ valgrind -q ./bin/05_heap_overflow (heap addresses representative)
==58983== Invalid write of size 1
==58983== at 0x4004C0: main (05_heap_overflow.c:90)
==58983== Address 0x4a6a048 is 0 bytes after a block of size 8 alloc'd
==58983== at 0x4840B26: malloc (vg_replace_malloc.c:447)
==58983== by 0x4004A8: main (05_heap_overflow.c:87)
How to read it:
"Invalid write of size 1" -> a 1-byte store to memory it
shouldn't touch.
"0 bytes after a block of size 8" -> the address is immediately after
an 8-byte heap block => overflow
by one. valgrind even shows the
malloc call site (line 87).$ clang -g -O0 -fsanitize=address 05_heap_overflow.c -o bin/05_heap_overflow.asan
$ ./bin/05_heap_overflow.asan
==58990==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7b46f87e0018 at pc 0x4e9f06
WRITE of size 1 at 0x7b46f87e0018 thread T0
#0 0x4e9f05 in main .../05_heap_overflow.c:90:10
0x7b46f87e0018 is located 0 bytes after 8-byte region [0x7b46f87e0010,0x7b46f87e0018)
allocated by thread T0 here:
#0 0x4a5bd8 in malloc
#1 0x4e9e58 in main .../05_heap_overflow.c:87:15
ASan and valgrind agree: a 1-byte WRITE 0 bytes after an 8-byte block,
at line 90, allocated at line 87.ROOT CAUSE vs CRASH SITE: There is no crash site on the plain build — that's the danger. The root cause is the off-by-one index p[8] against a size-8 allocation. The tools tie the bad WRITE (line 90) back to the ALLOCATION (line 87), which is exactly the pair you need to reason about a heap overflow.
heap error / buffer overflow
2 ------------------------------------------------------------
`p = malloc(8)` gives 8 valid bytes (p[0..7]) but we write p[8] — one byte PAST the block. Buggy line: `p[8] = '!';`.
Usually SILENT on a plain run: a 1-byte overflow lands in the allocator's padding, so it "works" and exits 0. The corruption only bites later (heap metadata, the next block). valgrind and ASan both pinpoint it immediately.
Either allocate room for the index you use (`malloc(9)`), or don't write past the end (`p[7] = '!';`). Size allocations to the data, and index strictly < size.
Change `malloc(8)` to `malloc(9)` and keep `p[8]='!'`. Both valgrind and ASan go quiet: the access is now in bounds. Then make it `p[9]` and watch them fire again, now "1 bytes after a block of size 9". ============================================================
#include <stdlib.h>
#include <stdio.h>
int main(void) {
char *p = malloc(8); /* 8 bytes: valid p[0..7] */
p[0] = 'o';
p[7] = 'k';
p[8] = '!'; /* <-- THE BUG: write 1 byte past block */
printf("stored, p[7]=%c\n", p[7]);
free(p);
return 0;
}A lab is done when you can say what it showed on your machine — not when you have read it.
It decides which problems come first, which drills open, which labs you see and how the Learn surface is organised. Change it any time from the switch in the top bar, or press Escape to look around first.