What makes running off the end of a thread's stack a fault rather than silent corruption of the memory next to it?
answer
- an untouchable page just past the limit
- first touch traps, nothing is corrupted
- detection is per access, not per frame
- a very large frame can step over it
- probes touch each page walking down
basics
~20 sA guard page: one or more pages with no access rights placed immediately past the stack's limit. The first access that lands on it traps, so the runtime reports an overflow instead of letting the write land in whatever is mapped beyond. Detection is per access, not per frame.
solid answer
~50 sImmediately beyond a thread's stack limit sits a **guard page** - address space that is mapped with no access permission. Growing the stack past the limit means touching that page, which traps, and the fault handler turns what would have been a silent write into a reported overflow. The important subtlety is that the guard catches accesses that *land on it*. A frame with a very large local area moves the stack pointer down by more than the guard's width and may then write deep inside its own frame, past the guard entirely, onto memory that is mapped and writable. That is why compilers emit **stack probes** for large frames: the prologue touches each page as the pointer walks down, so the guard is hit rather than stepped over. Handling the fault also needs care, because the handler cannot run on the stack that just ran out.
go deeper
Know the idea: a page just past the end of the stack that cannot be touched, so overrunning the stack raises an error instead of quietly writing over something else.
Explain the mechanism through address translation - the page carries no access rights, the first access traps, and the same check lets the stack grow lazily up to its limit.
Show where the guarantee ends: detection is per access, so an unprobed large frame can clear the guard and write onto mapped memory, and the fault is not a recoverable condition.
Treat it as a boundary you design around: fat frames in recursive paths, probe settings and a working overflow report are what stand between a crisp crash and corruption diagnosed weeks later.
## What the guard is A thread's stack occupies a bounded range of addresses. Immediately past the far end, systems place a **guard page** - one or more pages of address space that exist but carry no access permission. Nothing is stored there; its entire job is to be untouchable. The arrangement converts a class of silent corruption into a deterministic fault. Without it, the frame that stepped past the limit would simply be writing into whatever happened to be mapped next: another thread's stack, a data region, or an allocator's structures. The program would keep running with two owners for the same bytes, and the eventual symptom would appear far from the cause. ## What happens on the first touch 1. A prologue moves the stack pointer down, or a write lands below the previous low-water mark. 2. The access falls on a page with no permission, and the hardware raises a fault. 3. The system inspects the faulting address. Where stacks grow on demand, an address just below the current stack but still inside the allowed range is treated as growth: the page is made available and the thread continues. 4. An address on the guard itself is outside the allowed range, so it is reported as a stack overflow rather than backed. That step 3 is why the same mechanism serves two purposes - extending the stack lazily up to the limit, and stopping it dead at the limit. ## Why a very large frame can step over it The guard only works on accesses that **land on it**. Consider a frame that reserves far more than the guard's width: - The prologue subtracts the whole frame size from the stack pointer in one move, without touching anything. - The body then writes near the top of its own locals - an address well past the guard. - Nothing ever touched the guard, so nothing trapped, and the write went to memory that is mapped and writable. The mitigation is a **stack probe**: the prologue touches one location per page as it walks the pointer down across a large frame, guaranteeing that the guard is hit if the frame crosses it. Probing costs a few instructions per page, so it is usually emitted only for frames above a threshold - which is exactly why the danger is specific to unusually fat frames rather than to ordinary ones. | | ordinary frame | frame larger than the guard | |---|---|---| | how the pointer moves | a small step, well inside the guard's width | one large step that can clear it | | what touches the guard | the next push or write does | possibly nothing at all | | result at the limit | a reported overflow | a write onto unrelated mapped memory | | mitigation | none needed | per-page probes in the prologue | ## The handler needs stack of its own A fault handler is code, and code needs somewhere to put its frame. Running it on the stack that has just been exhausted would fault again immediately. Systems solve this in one of two ways, and they genuinely differ: some reserve a separate area for the handler and switch to it when a fault arrives; others keep a small reserve below the limit, re-armed after the report so a second overflow is still caught. Either way, an environment with neither arrangement turns a reportable overflow into a thread that dies without a message. ## What the guard promises and what it does not - **It promises** that the ordinary case - a normal recursion walking past its limit one small frame at a time - is detected at the boundary, before anything outside the stack is written. - **It does not promise** that every overflow is detected: detection is per access, and an unprobed large frame can jump the guard. - **It does not give you** a recoverable condition. The fault arrives at an arbitrary instruction, so the thread's invariants may be half-updated; the honest response is to fail that unit of work. - **It is not spare capacity.** The guard is not stack the thread grows into; touching it is the failure, not a step before it. - **It costs almost nothing.** No permissions are checked per write by software; the page's absent permission does the work through the same address-translation machinery every access already uses.
- What does a stack probe in a function prologue do, and why is it not emitted everywhere?It touches one address per page as the stack pointer walks down across a large frame, so a frame crossing the guard is guaranteed to land on it and trap. It costs a few instructions per page, so toolchains usually emit it only for frames large enough to clear the guard on their own.
- Why does handling a stack-overflow fault need a special arrangement?The handler cannot run on the stack that has just been exhausted, so systems either switch to a separately reserved handler area or keep a small reserve below the limit and re-arm it after reporting. Without one of those, the handler faults again and the thread dies with no diagnostic at all.
saying these in an interview costs you the question
- Thinks every overflow is detected, whatever the frame's size
- Believes the guard page is spare capacity the stack grows into
- Expects the fault handler to run on the stack that just overflowed
- Assumes an overflow can only ever corrupt the failing thread's own memory
- Confuses the guard with a software bounds check performed on every write