5.5.24 · D4Embedded Systems & Real-Time Software

Exercises — Memory protection units (MPU) — preventing stack overflow, access faults

4,151 words19 min readBack to topic

Before we start, one visual to fix the vocabulary we will reuse in every problem.

Figure — Memory protection units (MPU) — preventing stack overflow, access faults

Walk through the picture slowly — every term you will meet all page lives on it.

  • The tall arrow on the left is the address axis: it points up toward higher addresses and down toward lower ones. Memory is just a numbered ladder of bytes; this arrow is that ladder stood on end.
  • The shaded blue box is a region — a single contiguous slice of that ladder. One region = one continuous run of addresses with one set of rules (can you read it? write it? run code from it?).
  • The yellow line at the bottom of the box is the base address: the lowest address in the region, i.e. where the slice starts.
  • The pink dashed line at the top is BASE + SIZE: the first address just past the region. Note it is one past the top, not the last byte inside — the region owns addresses with (top excluded).
  • The double-headed arrow up the left edge of the box is the size: how tall the slice is, always a power of two, bytes.

So a region is fully described by just two numbers — where it starts (base) and how tall it is (size) — plus its rules. The alignment rule "base must be a multiple of the size" is the one thing this figure does not yet justify; we earn why below in #L2 Application.


L1 Recognition

Problem 1.1 (L1) — Decode a size field

On ARM Cortex-M, the region size is encoded in the RASR (Region Attribute and Size Register) as a field called SIZE. The actual byte-size of the region is

The SIZE field is written as (k << 1) in the parent's code. As explained in the definition above, k << 1 places the numeric value into the SIZE field that begins at bit 1 of RASR, so the field's value is . If the code writes (9 << 1), so SIZE = 9, how many bytes is the region?

Recall Solution 1.1

The field value is . Plug in: What we did: substituted the field value into the size formula. Why: the hardware stores the exponent minus one to save bits — one 5-bit field covers everything from (32 B) up to (4 GB). So a stored means an exponent of , i.e. bytes. ✅

Problem 1.2 (L1) — What does AP = 000 mean, and what are the other codes?

The AP (Access Permission) field in RASR is written as (v << 24) — three bits placed starting at bit 24. The parent note used (0 << 24) for the guard region. In words, what access does AP = 000 grant, and what are the other legal codes?

Recall Solution 1.2

AP = 000 means No access — neither privileged nor unprivileged code may read or write this region. That is exactly what a guard band wants: the first touch faults. The full ARMv7-M AP encoding table (Armv7-M Architecture Reference Manual, section on the MPU) is:

AP Privileged Unprivileged
000 No access No access
001 RW No access
010 RW RO (read-only)
011 RW RW
100 reserved (unpredictable)
101 RO No access
110 RO RO
111 RO RO

Edge cases to know:

  • Code 100 is the only reserved code — writing it gives unpredictable behaviour, so never use it.
  • Code 101 is not reserved; it is a normal, architecturally-defined code meaning "privileged read-only, no user access" — handy for read-only kernel data. Do not confuse the reserved 100 with the perfectly legal 101; only the single value 100 is off-limits.
  • Codes 110 and 111 both mean "read-only for everyone" (they are equivalent; hardware treats them the same).

The parent used only 000, 001, and 011, but a real driver often needs 010 (kernel writes, user reads) and 110/111 (read-only constants / flash). ✅


L2 Application

A programmer wants a 64-byte () region and writes base address 0x20000010. Is this base legal (properly aligned), and if not, where does the hardware actually place the region?

Recall Solution 2.1

Alignment rule: for a -byte region, the base's low bits must all be zero (base is a multiple of the size). Here , so the low 6 bits must be zero, i.e. base must be a multiple of 64.

0x20000010 = 0x20000000 + 0x10 = base + 16. Is 16 a multiple of 64? No. The low 6 bits of 0x...10 are 010000 — not all zero. So the base is illegal.

Where it lands: the hardware masks off the low 6 bits. Recall ~ flips every bit (see the definition at the top): ~(64 - 1) = ~0x3F is "all ones except the low 6 bits", so 0x20000010 & ~0x3F = 0x20000000. Your protection window silently slides down to 0x20000000–0x20000040, not the 0x20000010–0x20000050 you intended.

What it looks like: see the figure below — the intended window (pink) versus the actual masked window (blue). ✅

Figure — Memory protection units (MPU) — preventing stack overflow, access faults

Problem 2.2 (L2) — Compute an aligned base

You must protect the 32-byte guard band that sits at the very bottom of a stack whose bottom is at 0x20000C00. Using base = addr & ~(size - 1), what base do you write for a 32-byte guard?

Recall Solution 2.2

Size , so size - 1 = 31 = 0x1F. The mask is ~0x1F = "all ones, low 5 bits clear". The low 5 bits of 0x20000C00 are already zero (, last 5 bits 00000), so masking changes nothing: Why it was already aligned: 0xC00 = 3072, and exactly — a whole number of 32-byte blocks. Good stack layouts are chosen so guards land on such boundaries. See Stack memory layout. ✅


L3 Analysis

Problem 3.1 (L3) — Which region wins on an overlap?

Region 0: BASE = 0x20000000, SIZE = 2^10 (1 KB), AP = RW. Region 1: BASE = 0x20000000, SIZE = 2^5 (32 B), AP = No access (guard).

A task writes to address 0x20000004. Which region's rules apply, and does the write succeed or fault?

Recall Solution 3.1

Step 1 — which regions match? A region matches if .

  • Region 0: matches.
  • Region 1: matches (4 < 32).

Step 2 — resolve the tie. Both match. As justified in the callout above, the hardware picks the highest-numbered matching region so specific exceptions override broad backgrounds. Region 1 > Region 0, so Region 1 wins.

Step 3 — apply Region 1's rule. Region 1 is No access. The write faults → CPU vectors to MemManage_Handler(). That is exactly the desired guard behaviour. ✅

See Memory faults and exception handling for what the handler does next.

Problem 3.2 (L3) — The same layout with the numbers swapped

Now suppose someone accidentally puts the guard in Region 0 (32 B, No access) and the broad RW stack in Region 1 (1 KB, RW). Same write to 0x20000004. Does the guard still protect the bottom?

Recall Solution 3.2

Both regions still match 0x20000004 (same address ranges as before). But now the highest-numbered matching region is Region 1, which is RW. So Region 1 wins, the write succeeds, and the guard in Region 0 is silently ignored.

Conclusion: the guard's protection depends entirely on it having the higher region number. Numbering, not order-of-configuration, decides priority. This is the layering rule from the callout used wrong: the broad background must be low-numbered, the specific exception high-numbered. ✅

Figure — Memory protection units (MPU) — preventing stack overflow, access faults

L4 Synthesis

Problem 4.1 (L4) — Design a two-task isolation layout

You have 4 KB of RAM starting at 0x20000000. Design an MPU layout with:

  • Task A stack: 1 KB, RW, with a 32-byte no-access guard at its bottom.
  • Task B stack: 1 KB, RW, with a 32-byte no-access guard at its bottom.
  • All stacks must be XN (execute-never).
  • Every base aligned to its size; guards must win over their stacks.

Give the base address, size field value, AP, and region number for each region.

Recall Solution 4.1

Layout (stacks grow downward, so the guard sits at the low end of each stack):

Region Base Size SIZE field AP XN Purpose
0 0x20000000 1 KB () 9 RW (011) 1 Task A stack
1 0x20000000 32 B () 4 No access (000) 1 Task A guard
2 0x20000400 1 KB () 9 RW (011) 1 Task B stack
3 0x20000400 32 B () 4 No access (000) 1 Task B guard

Checks:

  • Alignment: 0x20000000 is a multiple of 1024 (it's 0) and of 32 → ✅. 0x20000400 = 1024, a multiple of 1024 and 32 → ✅.
  • Priority: each guard (regions 1, 3) has a higher number than its stack (regions 0, 2). ✅ (per Problem 3.1).
  • XN: all four have XN = 1, blocking code execution from any stack — defeats stack shellcode (see the XN definition above). See Buffer overflow attacks.
  • SIZE field: 1 KB → → field ; 32 B → → field . ✅ (per Problem 1.1).

Total RAM used: B of the 4 KB — plenty of room. The guards overlap the stack bottoms, so they cost no extra RAM.

Why an overlapping guard costs no extra RAM: an MPU region is not an allocation — it does not reserve or consume bytes. It is only a rule the MPU checks on each access: "for addresses in this range, apply these permissions." The guard region and the stack region are two rules that happen to describe the same physical bytes (the stack's low 32). Those bytes were already counted inside the 1 KB stack; the guard just changes the verdict on them (no-access instead of RW), it does not claim new memory. Regions are overlays, not reservations. The only real cost is that those 32 overlaid bytes become unusable as stack — see Problem 5.1. ✅

This is exactly the pattern RTOS task switching reprograms on every context switch: swap regions 0–1 for the incoming task's stack + guard.


L5 Mastery

Problem 5.1 (L5) — Latest-safe stack size before the guard fires

Task A's region is 0x20000000–0x20000400 (1 KB) with a 32-byte guard at 0x20000000–0x20000020. The stack pointer starts at the top (0x20000400) and grows downward. A function allocates a local array of bytes in one shot (char buf[S]), pushing the SP down by .

(a) What is the largest that does not touch the guard? (b) What is the smallest that does trigger a fault?

Recall Solution 5.1

The guard occupies addresses with , i.e. bytes 0x000x1F. The stack write of bytes touches [0x20000400 - S, 0x20000400).

(a) It stays out of the guard iff its lowest written byte is at or above the guard's top, i.e. , which rearranges to .

(b) The smallest fault-triggering allocation is one byte more: At , the lowest written byte is , which is in the guard → MemManage fault. ✅

Takeaway: the guard steals 32 bytes of nominal stack, so budget your worst-case stack depth against B, not B.

Problem 5.2 (L5) — How much earlier does a bigger guard catch it?

Compare a 32-byte guard against a 128-byte guard on the same 1 KB stack. By how many bytes of overflow sooner does the 128-byte guard catch a downward overflow, and what is the RAM cost?

Recall Solution 5.2

Set up one clean model and stick to it. A guard of bytes occupies [0x20000000, 0x20000000 + G). Its top is at address 0x20000000 + G. A downward-growing stack faults as soon as its lowest written byte drops below that top, i.e. into the guard. So the single quantity that matters is the guard-top address — the higher it sits, the sooner (with less descent) the overflow hits it.

  • 32-byte guard: guard-top = 0x20000020. Fault when the write reaches below 0x20000020.
  • 128-byte guard: guard-top = 0x20000080. Fault when the write reaches below 0x20000080.

How much sooner? Take the difference of the two guard-top addresses: The 128-byte guard's top sits 96 bytes higher, so the descending stack meets it after 96 fewer bytes of overflow — it fires 96 bytes earlier.

RAM cost: the guard overlaps the stack (it is a rule, not an allocation — see Problem 4.1), so it does not add RAM — but it removes usable stack (as we found in Problem 5.1, usable ):

  • 32-byte guard leaves B usable.
  • 128-byte guard leaves B usable.

You trade 96 bytes of usable stack for 96 bytes of earlier detection — the two are equal, which makes sense: every byte you add to the guard is one byte the top rises and one byte of usable stack you lose. On tight RAM, keep the guard at the 32-byte minimum; when chasing an elusive overflow bug, temporarily widen it. ✅


Recall Self-test summary (cover the right column)

Field value 9 encodes how many bytes? ::: B What does k << 1 do to the value k? ::: Shifts its bits left by 1 (= ), placing it in the field starting at bit 1. What does ~x do? ::: Bitwise NOT — flips every bit; used to build alignment masks. What do RBAR and RASR stand for? ::: Region Base Address Register and Region Attribute and Size Register. Does a misaligned base fault? ::: No — the hardware masks low bits and protects the wrong range silently. On overlap, which region wins and why? ::: Highest-numbered — so specific high-index exceptions override broad low-index backgrounds. Which AP code is reserved? ::: Only 100 (unpredictable — never use it); 101 is a legal privileged-RO code. Usable stack in a 1 KB region with a 32 B guard? ::: 992 bytes. AP = 000 means? ::: No access (privileged or unprivileged). Why does an overlapping guard cost no extra RAM? ::: A region is a permission rule, not an allocation — it re-describes bytes the stack already owns. What does XN = 1 forbid, and why on stacks? ::: Instruction fetch (execute); stops shellcode on an overflowed stack from running.