$ clang -g -O0 08_memory_leak.c -o bin/08_memory_leak
$ ./bin/08_memory_leak ; echo "exit=$?"
used p[0]=0x55; now leaking 100 bytes
exit=0
^ Looks perfectly healthy. That's why leaks survive code review.$ clang -g -O0 08_memory_leak.c -o bin/08_memory_leak
$ ./bin/08_memory_leak ; echo "exit=$?"
used p[0]=0x55; now leaking 100 bytes
exit=0
^ Looks perfectly healthy. That's why leaks survive code review.There is no crash, so `gdb run` just exits 0 — gdb shows you nothing. gdb is for FAULTS (signals, wrong values), not for "memory that was never given back". For leaks, go straight to valgrind --leak-check or LSan.
$ valgrind -q --leak-check=full --show-leak-kinds=all ./bin/08_memory_leak
==61827== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==61827== at 0x4840B26: malloc (vg_replace_malloc.c:447)
==61827== by 0x400488: main (08_memory_leak.c:77)
How to read it:
"100 bytes in 1 blocks" -> size and count of the leaked allocation.
"definitely lost" -> no pointer to it survives => a true leak.
"at malloc ... by main (...:77)" -> the ALLOCATION call stack: the
leak is the block born at line 77. valgrind points at where it was
allocated (you decide where it SHOULD have been freed).
--leak-check=full -> per-leak stack traces (not just a summary count).
--show-leak-kinds=all -> also list "indirectly lost", "possibly lost",
and "still reachable" blocks, not only "definitely lost".
THE FOUR LEAK KINDS:
definitely lost -> no pointer at all => fix it.
indirectly lost -> only reachable via an already-lost block.
possibly lost -> only an interior pointer remains (suspicious).
still reachable -> a pointer exists at exit (often "intentional",
e.g. a global cache) — usually benign.
ASan/LeakSanitizer finds it too, automatically at process exit:$ clang -g -O0 -fsanitize=address 08_memory_leak.c -o bin/08_memory_leak.asan
$ ./bin/08_memory_leak.asan
==61836==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 100 byte(s) in 1 object(s) allocated from:
#0 0x... in malloc
#1 0x... in main .../08_memory_leak.c:77:15
SUMMARY: AddressSanitizer: 100 byte(s) leaked in 1 allocation(s).
ROOT CAUSE vs CRASH SITE:
No crash site at all. The "root cause" is a missing free on the path that
loses the last pointer. The tool reports the ALLOCATION site (line 77)
because that's the block's identity; the FIX lives wherever ownership
ends.leak
1 ------------------------------------------------------------
`malloc(100)` allocates a block that is never `free`d. When `p` goes out of scope at return, the only pointer to the block is lost — the memory stays allocated until the process exits. Buggy line: `char *p = malloc(100);` (with no free).
NONE at runtime: exits 0, output normal. A leak is invisible to a single run; it only hurts long-running programs (memory grows without bound). You need a tool to even see it.
Free what you allocate, on every path: free(p); (For CI, see --error-exitcode in the README so a leak fails the build.)
Add `free(p);` before return — both valgrind and LSan go silent ("All heap blocks were freed -- no leaks are possible"). Then `free(p); free(p);` turns it into bug #07 (double free). ============================================================
#include <stdlib.h>
#include <stdio.h>
int main(void) {
char *p = malloc(100); /* <-- THE BUG: never freed */
p[0] = 0x55;
printf("used p[0]=0x%02x; now leaking 100 bytes\n", (unsigned char)p[0]);
return 0; /* p lost here: 100 bytes leak */
}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.