Visual walkthrough — Memory protection units (MPU) — preventing stack overflow, access faults
Every symbol used below is defined the moment it appears. No prior MPU knowledge is assumed.
Step 1 — What memory actually is: a numbered street of boxes
WHAT. Before "protection" means anything, we need to agree what is being protected. Memory is a long line of tiny boxes, each holding one byte (a byte = a group of 8 on/off switches, enough to store one number from 0 to 255). Each box has a permanent house-number called its address. We write addresses in hexadecimal (base-16), with a 0x in front — so 0x20000000 is just "box number 536,870,912", written compactly.
WHY. The MPU's entire job is to answer one yes/no question — "is the CPU allowed to touch box number right now?" — so the address is the only input we care about. Everything downstream is a comparison against .
PICTURE. Look at the street of boxes. On the RAM chip, low addresses are drawn on the left, high addresses on the right. The magenta arrow marks one particular address the CPU wants to access.

See Stack memory layout for how these boxes are grouped into a stack.
Step 2 — The stack: a pile that grows downward
WHAT. A stack is the region of RAM a task uses for its local variables and return addresses. The stack pointer (call it SP) is a special CPU register — a register is a tiny ultra-fast storage box inside the CPU — that always holds the address of the current top of the pile. On ARM Cortex-M, pushing new data makes SP decrease: the stack grows toward lower addresses.
WHY. This one fact — grows downward — decides where we must put the fence. If the stack grew up we'd fence the top; because it grows down, an overflow crashes through the bottom (lowest) address. So the guard belongs at the bottom.
PICTURE. Task A owns bytes 0x20000000 to 0x20000400 (that span is bytes KB). The violet SP arrow starts high and each push slides it down. The danger zone is the bottom edge at 0x20000000.

Step 3 — A region: the fence the MPU understands
WHAT. The MPU cannot fence arbitrary shapes. It fences regions. A region is described by just three numbers:
- BASE — the lowest address the region covers.
- SIZE — how many bytes it spans (must be a power of two, , with so the smallest region is bytes).
- permissions — what you may do inside: read, write, execute, and whether ordinary (unprivileged) tasks are allowed or only the kernel.
WHY. Powers of two, and only powers of two, let the hardware test membership with a bit-mask instead of slow arithmetic (we prove that in Step 4). That constraint is not laziness — it is what makes the check fast enough for a hard real-time system where every access must be decided in a fixed, tiny number of clock cycles.
PICTURE. One region drawn as a bracket over the street: its left edge is BASE, its width is SIZE, and a little tag lists its permissions.

Step 4 — Why BASE must be aligned: the bit-mask trick
WHAT. We now show why BASE must be a multiple of SIZE. Take a region of size . The hardware tests " is inside" by checking whether the top bits of equal the top bits of BASE, ignoring the low bits. In symbols, with & meaning bitwise-AND:
WHY. SIZE - 1 is a string of ones in binary (e.g. ). The tilde ~ flips every bit, giving a mask that keeps the high bits and zeroes the low . AND-ing with it snaps down to the nearest multiple of . Comparing that to BASE is a single-cycle operation — no adder, no loop. But this only works if BASE itself has its low bits zero; otherwise the hardware silently uses BASE & ~(SIZE-1) and your fence shifts to somewhere you never intended (the parent's Mistake 1).
PICTURE. The binary of 0x20000000 for a 32-byte region: the top bits (magenta) are the region tag, the bottom 5 bits (orange) are forced to zero. Set one of those low bits and the fence teleports.

Step 5 — Two regions on the same spot: who wins?
WHAT. We deliberately place two overlapping regions on the stack bottom:
- Region 0 = the whole 1 KB stack, permission RW (read/write allowed).
- Region 1 = the bottom 32 bytes, permission no access.
Both regions match any address in that bottom sliver. The MPU's tie-break rule is: the highest-numbered matching region wins.
WHY. We want the restrictive fence to override the permissive one exactly there. Numbering the guard higher (Region 1 > Region 0) makes its "no access" the final verdict in the overlap — while the rest of the stack, matched only by Region 0, stays RW. Swap the numbers and the guard is silently ignored (the parent's Mistake 2).
PICTURE. Region 0 spans the whole stack in violet; Region 1 (magenta) sits over just the bottom 32 bytes. In the overlap the magenta verdict wins.

Step 6 — The overflow: the first byte over the edge faults
WHAT. Task A pushes too much — a char buf[2048] in a 1 KB stack. SP slides down past 0x20000020, then the code writes to 0x2000001F, which is inside the bottom-32 guard. The MPU runs the check: Region 1 matches, its permission is no access, the access is a write → violation.
WHY. The value here is immediacy. Without the guard, that write would have silently landed in a neighbour's memory and detonated hours later inside a different task. With the guard, the very first illegal byte is caught, at the exact instruction that caused it — turning an impossible bug into a one-line stack trace. See Buffer overflow attacks for the attacker's version of the same overflow.
PICTURE. The SP arrow crosses the orange guard line; a burst marks the faulting write at 0x2000001F.

Step 7 — What the fault does: vectoring to the handler
WHAT. The MPU aborts the write before it changes memory and signals a MemManage fault exception. The CPU stops the task, saves its state, and jumps to a fixed function MemManage_Handler(). A hardware register named MMFAR (MemManage Fault Address Register) now holds the offending address 0x2000001F, so the handler knows exactly what and where.
WHY. This is the bridge from "hardware said no" to "software decides what next" — kill the task, log it, or reboot. Because the jump is a fixed vector with fixed latency, the response time is deterministic, which is why an MPU (not a general MMU) suits exception-driven real-time code. During an context switch the RTOS also reprograms the regions so each task gets its own fences.
PICTURE. A control-flow jump: task instruction → MPU veto → vector table → MemManage_Handler, with MMFAR displayed alongside.

The one-picture summary
Everything above, compressed: the downward-growing stack, the two nested regions, the highest-number-wins verdict, the first overflowing byte, and the arrow into the fault handler — one canvas.

Recall Feynman retelling — say it back in plain words
Memory is a street of numbered boxes; the MPU is a guard who is shown a box number before every touch and must say allowed or no. The guard can only fence stretches that are powers of two long and start on a matching round number — because it checks membership by masking the low bits, which is why misaligning the start teleports the fence. A task's stack grows toward lower box numbers, so I fence its bottom. I lay two fences on the same spot: a wide one over the whole stack saying "read/write fine", and a tiny 32-byte one over the very bottom saying "nobody touches this". When two fences overlap, the one with the bigger number wins — so I give the tiny strict fence the bigger number, and it overrides "read/write" only right at the edge. The instant a runaway strcpy writes one byte into that bottom sliver, the guard says no, cancels the write before any memory changes, and yells "MemManage fault!". The CPU freezes the task, jumps to my handler, and hands me the exact address in MMFAR — so a corruption that used to surface hours later in a totally different task now stops dead at the guilty line.
Recall Quick self-check
Why must a 32-byte region's base have its low 5 bits zero? ::: Because membership is tested by masking the low bits where ; a nonzero low bit makes hardware snap the base down, moving the fence. The stack grows downward — which end gets the guard? ::: The bottom (lowest address), because that is where an overflow breaks through. Two regions match the same address — which wins on ARM? ::: The highest-numbered matching region. So the strict guard should be numbered higher or lower than the broad stack region? ::: Higher, so its "no access" overrides the broad "RW". Which register tells the handler the faulting address? ::: MMFAR.