# Stack Canaries: How the Compiler Catches a Buffer Overflow

> A classic stack smash overwrites the return address and hijacks execution. Stack canaries are the cheap, clever defense your compiler inserts automatically — and understanding them explains a whole class of exploits.

URL: https://harshith.in/blog/stack-canaries/
Author: Harshith Sunku (Staff Engineer) — https://harshith.in/about/
Tags: security, systems

The oldest trick in memory-corruption exploitation is overflowing a local buffer
to overwrite the saved return address, redirecting the CPU into attacker code.
The stack canary is the defense that's been quietly compiled into your binaries
for two decades. It doesn't prevent the overflow — it makes the program notice
before it's too late.

## The Stack Layout Being Attacked

On a function call, the stack holds the local buffer, saved registers, and the
return address the CPU will jump to when the function returns:

```text
   higher addresses
   ┌────────────────────┐
   │  return address    │  ← overwrite this to hijack control
   │  saved frame ptr   │
   │  ...               │
   │  char buf[64]      │  ← overflow starts here, grows upward
   └────────────────────┘
   lower addresses
```

`gets(buf)` or an unchecked `strcpy` writes past `buf`, marches up through saved
registers, and clobbers the return address. On `ret`, the CPU jumps wherever the
attacker pointed.

## The Canary

The compiler inserts a secret random value — the **canary** — between the local
buffers and the return address. It's written on entry and checked right before
return:

```text
   ┌────────────────────┐
   │  return address    │
   │  canary  (random)  │  ← any overflow reaching the return addr
   │  char buf[64]      │     must first overwrite this
   └────────────────────┘
```

A contiguous overflow that wants to reach the return address *must* trample the
canary on the way. Before returning, the function compares the stored canary to
the known-good value:

```text
   __stack_chk_fail() if  stack_canary != fs:0x28
```

Mismatch → the program aborts immediately instead of returning into corrupted
control flow. You've seen the result: `*** stack smashing detected ***`.

## Why It's Cheap and Where It Fails

The canary value lives in a per-thread location (the TLS, `fs:0x28` on x86-64)
and is randomized per process, so an attacker can't just hardcode it. The runtime
cost is a couple of instructions per protected function — the compiler only adds
it to functions with stack buffers (`-fstack-protector-strong`).

It is not a cure. Attackers bypass canaries by:

- **Leaking the canary** via a separate info-leak bug, then writing it back
  intact.
- **Overwriting non-contiguously** — e.g. a bad array index that skips the canary
  and hits the return address directly.
- **Targeting other pointers** (function pointers, GOT entries) that don't sit
  behind the canary.

That's why canaries ship alongside ASLR, NX/DEP, and CFI — defense in depth, not
a single wall.

## Takeaways

- A stack canary is a random value placed between local buffers and the return
  address, checked before every protected return.
- A linear overflow can't reach the return address without destroying the canary
  first, so the program aborts instead of being hijacked.
- It's defeated by info leaks and non-linear writes — which is why it's one layer
  among ASLR, NX, and CFI, not the whole defense.
