A stack canary (or stack cookie) is a runtime-random value placed on the stack between the local variables and the saved base and return pointers. On function exit the program compares the canary on the stack against its original value; a mismatch means the stack was overwritten between entry and return, so __stack_chk_fail is called and the process aborts before the corrupted saved return address can be jumped through.
Mechanism
Placement and check
On function entry the compiler emits a prologue that copies a per-process canary from its storage slot (fs:28 on x86-64) onto the stack, just above the locals and just below the saved rbp/rip:
... input ← local buffer ... 0x12bd90cf4390f700 ← canary (copied from fs:28 on entry) 0xf680 ← saved rbp main+45 ← saved rip
On exit, the function takes the canary from the stack and cmps it against fs:28; if they differ it calls __stack_chk_fail, which exits the program. Any overflow that walks from the buffer upward into the saved rip must walk through the canary first, and is therefore detected before the corrupted return is taken.
Properties
What makes the canary useful
random per execution, constant within one execution — the canary is generated at process start and stored once in fs:28; the same value is reused across all function calls in that run, but a different value on the next run;
least-significant byte is 0x00 — buffers read through string functions stop at a null terminator, so the 0x00 byte foils the simplest buffer-over-read that would otherwise leak the canary byte-by-byte;
recompilation required — the canary code is emitted by the compiler, so existing binaries must be recompiled to enable it.
Bypass
Three ways around the canary
Leaking the canary. A partial overwrite reaching only the least-significant (non-null) bytes of the canary, paired with a string printing function, leaks the canary to stdout. Leaked in one call, reused in the next.
Overriding __stack_chk_fail. If the attacker can redirect the failure handler itself — which requires other primitives, e.g. a writable GOT entry — the canary mismatch no longer aborts. This needs a second bug to chain.
Bypassing the canary with a write primitive. If the binary contains an *a = b instruction where both a and b are attacker-controlled (e.g. reachable through a ROP gadget like mov [rax], rdx; ret), the attacker can overwrite the saved ripwithout touching the canary at all — the write goes directly through the pointer, not by walking the stack. The canary defends against linear overflows, not against arbitrary writes.