$ clang -g -O0 11_infinite_recursion.c -o bin/11_infinite_recursion
$ ./bin/11_infinite_recursion ; echo "exit=$?"
Segmentation fault (core dumped)
exit=139$ clang -g -O0 11_infinite_recursion.c -o bin/11_infinite_recursion
$ ./bin/11_infinite_recursion ; echo "exit=$?"
Segmentation fault (core dumped)
exit=139$ gdb -q -batch -ex 'set debuginfod enabled off' -ex run \
-ex 'bt 4' -ex 'echo ...\n' ./bin/11_infinite_recursion
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400481 in recurse (depth=261945) at 11_infinite_recursion.c:81
#0 0x0000000000400481 in recurse (depth=261945) at 11_infinite_recursion.c:81
#1 0x0000000000400486 in recurse (depth=261944) at 11_infinite_recursion.c:81
#2 0x0000000000400486 in recurse (depth=261943) at 11_infinite_recursion.c:81
#3 0x0000000000400486 in recurse (depth=261942) at 11_infinite_recursion.c:81
(More stack frames follow... a quarter-million of them.)
How to read it:
- The SAME function repeats forever in the backtrace -> runaway
recursion. That repetition IS the diagnosis.
- The `depth=` values count DOWN as you go up the stack (261945, 261944,
261943...), so the deepest live call had depth ~261945: that's how many
frames deep it got before exhausting the 8 MiB stack. (Representative —
yours WILL differ; sometimes frame #0's arg even reads as "<error
reading variable: Cannot access memory>" once SP passes the guard.)
- The crash SITE is just wherever the stack happened to run out — its
address/depth is meaningless. The repeating frame is the real signal.
- `bt 4` limits the dump; a full `bt` here prints ~261945 frames. Use
`bt N` (innermost N) or `bt -N` (outermost N) on deep stacks so gdb
doesn't drown you.
ASan reports it crisply as a stack-overflow:$ clang -g -O0 -fsanitize=address 11_infinite_recursion.c -o bin/11_infinite_recursion.asan
$ ./bin/11_infinite_recursion.asan
==NNNNN==ERROR: AddressSanitizer: stack-overflow on address 0x.... ...
#0 ... in recurse .../11_infinite_recursion.c:81$ valgrind -q ./bin/11_infinite_recursion
==NNNNN== Stack overflow in thread #1: can't grow stack to 0x1ffe801000
(Then a SIGSEGV with a very deep stack trace. memcheck CAN flag stack
exhaustion, but the giant gdb backtrace is the fastest way to SEE that a
single function is eating the stack.)
ROOT CAUSE vs CRASH SITE:
The crash SITE is an arbitrary deep frame where the stack finally ran out
— its exact address/depth is meaningless and changes every run. The ROOT
CAUSE is structural: recurse() has no terminating condition. Don't debug
the crash address; recognize the repeating frame and fix the recursion.stack error (stack exhaustion) / segfault
3 ------------------------------------------------------------
`recurse` calls itself with no base case, so it never returns. Each call pushes a new stack frame; after ~millions of frames the stack (8 MiB by default) is exhausted and the next push hits the guard page -> SIGSEGV. Buggy line: `return recurse(depth + 1) + 1;` (missing a stop condition).
"Segmentation fault (core dumped)", exit 139. Deterministic as a crash, but the DEPTH reached varies (ASLR/frame size). NOTE: the `+ 1` defeats tail-call optimization, so even -O2 would recurse for real here; at -O0 every call uses a frame.
Give the recursion a base case that is always reached: int recurse(int depth) { if (depth >= LIMIT) return 0; // base case return recurse(depth + 1) + 1; } Or rewrite as an explicit loop with bounded iterations.
Add `if (depth > 100000) return 0;`. It now returns normally. Then lower the stack limit with `ulimit -s 1024` (1 MiB) and watch the overflow happen at a SHALLOWER depth — proof the limit is the stack size, not the code. ============================================================
static int recurse(int depth) {
return recurse(depth + 1) + 1; /* <-- THE BUG: no base case ever stops it */
}
int main(void) {
return recurse(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.