FForge
← Labsdebug-05Runtime debugging●●○○

Heap buffer overflow

debug/05_heap_overflow.c3 steps
0/3 steps
evidence to leave with
ASan heap-buffer-overflow with allocation stack
walk it, one step at a time
run this
$ clang -g -O0 05_heap_overflow.c -o bin/05_heap_overflow
$ ./bin/05_heap_overflow ; echo "exit=$?"
what it printed on the machine that wrote this lab
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:

run this
$ gdb -q -batch -ex 'set debuginfod enabled off' \
what it printed on the machine that wrote this lab
      -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.)
run this
$ valgrind -q ./bin/05_heap_overflow      (heap addresses representative)
what it printed on the machine that wrote this lab
  ==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).
run this
$ clang -g -O0 -fsanitize=address 05_heap_overflow.c -o bin/05_heap_overflow.asan
$ ./bin/05_heap_overflow.asan
what it printed on the machine that wrote this lab
  ==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.

category

heap error / buffer overflow

difficulty

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

what's wrong

`p = malloc(8)` gives 8 valid bytes (p[0..7]) but we write p[8] — one byte PAST the block. Buggy line: `p[8] = '!';`.

symptom

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.

the fix

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.

take it further

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". ============================================================

read the source · debug/05_heap_overflow.c
#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; }
◇
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.