pointers-memory · intermediate · ~15 min

Detect a buffer overflow with a canary

The mechanism behind GCC's -fstack-protector.

Challenge

Implement a stack-canary check by hand: a buffer is bracketed by two "canary" words holding a known magic value. If either word changes, something has overrun the buffer.

The struct and API:

typedef struct { uint32_t canary_lo; char buf[16]; uint32_t canary_hi; } guarded_t;
void guarded_init(guarded_t *g);
int  guarded_check(const guarded_t *g);   /* 1 if both canaries intact, 0 otherwise */

Task

Implement two functions (no main — the grader calls them):

  • guarded_init sets both canary_lo and canary_hi to the magic value 0xDEADBEEFu (and zeroes buf).
  • guarded_check returns 1 if both canaries still equal 0xDEADBEEFu, otherwise 0.

Input

A pointer to a guarded_t. (The grader simulates corruption by writing to a canary field directly — no real buffer overflow occurs, so this is safe under -fstack-protector.)

Output

guarded_check returns 1 (both canaries intact) or 0 (either has changed). guarded_init returns nothing.

Example

guarded_t g; guarded_init(&g);
guarded_check(&g)        ->   1
g.canary_hi = 0xCAFEBABE;
guarded_check(&g)        ->   0

Edge cases

  • Both canaries intact: return 1.
  • Either canary changed (high or low): return 0.

Rules

  • No allocations; just set / compare fields. Check both canaries, using the u suffix so the literal matches the uint32_t field.

Why this matters

Transfer exercise: apply the lesson to a complete function contract.

Input format

A pointer to a guarded_t. (The grader simulates corruption by writing to a canary field directly — no real buffer overflow occurs, so this is safe under -fstack-protector.)

Output format

guarded_check returns 1 (both canaries intact) or 0 (either has changed). guarded_init returns nothing.

Constraints

g is non-NULL and live. Initialize both canaries and all 16 bytes; check returns 1 only if both markers match. The harness changes fields directly, without an actual overflow.

Starter code

#include <stdint.h>
#ifndef GUARDED_T_DEFINED
#define GUARDED_T_DEFINED
typedef struct { uint32_t canary_lo; char buf[16]; uint32_t canary_hi; } guarded_t;
#endif
void guarded_init(guarded_t *g);
int  guarded_check(const guarded_t *g);

Common mistakes

Checking only one canary or leaving part of the buffer uninitialized.

Edge cases to handle

Each canary damaged separately; both damaged; reinitialization; all buffer bytes zero.

Complexity

O(1).

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.