The Case of the Overwritten Byte
A Stack District Murder Mystery
It was a cold evening on Thread T0 when the call came in.
Detective AddressSanitizer — "San" to the few who'd earned the right — was halfway through a shadow byte audit when dispatch lit up: WRITE of size 4. Address 0x7ffec98480b8. Victim pronounced corrupted at the scene.
San grabbed his coat. He already didn't like this one. Stack District. The respectable part of memory. Not some heap back-alley where use-after-frees went down every other night. This was supposed to be safe territory.
He arrived at Frame 0x560887e013f8 — a modest stack frame belonging to one main(), resident of /tmp_amd/glass/export/glass/2/z5161594/1511tute/illegal_array.c, apartment 4. The beat cops had already roped off the scene with redzone tape: f1 f1 f1 f1 on the left, f3 f3 f3 f3 on the right. Standard stack protocol. Nobody was supposed to get past those.
And yet.
The victim was offset 72 — the first byte just past the boundary of a local variable the neighbors all called 'array'. A respectable buffer. Forty bytes. Ten integers wide. Occupied the space from offset 32 to offset 72, and never once caused trouble. Kept to itself. Held its values. Stayed within its allocation.
"Who found the body?" San asked.
A shadow byte stepped forward. f2 — a stack mid-redzone. One of the sentinels. "I was standing post right at the boundary, sir. Offset 72. That's my position. Nobody's supposed to write here. Then at oh-nine-hundred — line 9 — I felt it. A WRITE. Four bytes. Came right through me like I wasn't even there."
San examined the shadow map. The evidence was clean as a whistle:
00 00 00 00 00 [f2]
Five pristine, addressable eight-byte words — the array, all forty bytes accounted for — and then the victim: f2, the mid-redzone, trampled by a four-byte store that had no business being there.
San climbed the call stack.
The first name on the list was _start — address 0x560887cfe429. Old infrastructure. The kind of function that shows up at the bottom of every stack, does its job, and never asks questions. Boilerplate. Not the type to overflow anything.
Next up: __libc_start_main, frame 0x7fc8f71ac09a, out of libc-start.c:308. A bureaucrat. System function. Called main() per procedure and clocked out. Alibi checks out. Glibc doesn't get its hands dirty like this.
Which left one suspect.
main(). Address 0x560887e014c4. Line 9 of illegal_array.c.
San looked at the program counter. Looked at the base pointer. Looked at the stack pointer. All three pointed the same direction.
"It was you," San said. "You declared a ten-element array on line 4. And then on line 9, you wrote to index 10."
main() shifted. "I thought I had room—"
"You had indices zero through nine. Forty bytes. You went to offset 72 — four bytes past your allocation. You stomped a redzone. You corrupted the stack."
"It was just one integer—"
"It's always just one integer." San flipped his notebook shut. "That's what they all say. One off-by-one. One past the end. And the next thing you know, a return address is compromised and someone's running shellcode from a place it was never meant to run."
San filed the report:
SUMMARY: Stack-buffer-overflow. Victim: stack redzone at offset 72. Perpetrator:
main(), line 9,illegal_array.c. Motive: presumed off-by-one indexing. Weapon: a 4-byte WRITE with no bounds check. Case status: CLOSED. Program terminated.
He poured himself a cold cup of /dev/null and stared out the window at the shadow bytes stretching into the distance — row after row of 00s, quiet and addressable, blissfully unaware how close the corruption had come to spreading.
Tomorrow there'd be another one. There always was. A heap-use-after-free in some forgotten module. A global buffer overread in a parser nobody maintained. The shadows never slept, and neither did the bugs.
But tonight, the redzones held.
Tonight, the Sanitizer got there first.
— From the case files of Detective AddressSanitizer, Shadow Byte Division