Lab 01 — Debugging Dojo: Find the Planted Bug
Phase: 10 — Reliability & Debugging | Difficulty: ⭐⭐⭐⭐☆ | Time: 3–5 hours
Language: C11 | Hardware: none | Needs: a C compiler with ASan; optional gdb/valgrind
Concept primer:
../WARMUP.mdCh. 1–4,../HITCHHIKERS-GUIDE.md§1–§4.
Run
make # normal build — buggy may APPEAR to work (a latent bug)
make asan # AddressSanitizer build — points at the exact bug
make valgrind # alternative detector
make fixed # the corrected version, clean under ASan
0. The mission
Practice the method, not the answer: a deliberate memory-safety bug lives in buggy.c. Find it
with tools (ASan/gdb/valgrind), explain it, fix it, and write the RCA (RCA.md). The
point is that a normal build may run fine — latent memory bugs are why you need sanitizers, and why
"it works on my machine" is not "it's correct."
1. The exercise (don't peek at fixed.c first)
make && ./buggy— observe. It may print plausibly or crash, depending on your libc. That ambiguity is the lesson.make asan— AddressSanitizer reports heap-use-after-free atprint_all(buggy.c:45) with two stack traces: where the memory was freed and where it was used. That's the localization tools give you for free.- Confirm with
gdb ./buggy(run, backtrace) and/orvalgrind. - Find the root cause in
remove_reading(it frees a node without unlinking it → the previous node's->nextdangles → UAF on the next traversal, and a double-free at cleanup). - Fix it (unlink before free; a sentinel handles head-removal — see
fixed.c), and verifymake fixedis clean under ASan. - Write
RCA.md— the writeup is the deliverable. Drive the 5 Whys to the systemic cause (no ASan in CI, no list-mutation tests) and list owned action items.
2. Why this matters
This is the JD's #1 skill in miniature: a real, latent, close-to-hardware bug found by method and tools, not luck — and turned into a permanent fix via a blameless RCA. Memory-safety bugs are the top cause of CVEs and the hardest production crashes (they crash far from their cause); ASan/TSan in CI (Phase 11) is how you stop them.
3. Extensions
- Plant a race in a threaded version and find it with TSan (
-fsanitize=thread). - Debug a hung process: add a blocking
readon a pipe that never gets data; find it withstrace -p. - Core-dump analysis:
ulimit -c unlimited, crash it, thengdb ./buggy coreand backtrace. git bisect: make a repo with the bug introduced in a middle commit and bisect to it.
4. What this lab proves
You debug with method and the right tool, you fix root causes (not symptoms), and you close the loop with a blameless RCA + preventive action items. "Tell me about a hard bug you root-caused" becomes a real story (STAR — Phase 12), and "you suspect memory corruption — what tools?" becomes a demonstration.