The Bug in 60 Seconds

Apple's Memory Integrity Enforcement (MIE) is a three-layer hardware security stack: memory tagging (EMTE), read-only zones, and the Secure Page Table Monitor (SPTM). On M5 silicon, it was supposed to make kernel memory bugs unexploitable. CVE-2026-28952 proved otherwise.

The bug lives in _zalloc_ro_mut, the only function allowed to write to read-only zones. Its C signature:

void _zalloc_ro_mut(zone_id_t zone, void *target, size_t offset, const void *src, size_t len);

The pre-patch bounds check (macOS 26.4.1) looked like this in pseudocode:

uint64_t end = target + len;
if (end < target) { // overflow detection
    // wrap-handling path, uses `end` (already wrapped!)
    if (target >= ro_zone_lo && end <= ro_zone_hi)
        goto write_ok; // 🤡
}

If len is huge enough that target + len wraps past 2^64, end becomes a tiny number. The wrap-handling path compares this tiny end against ro_zone_hi—and passes. The function then calls memcpy(target, src, len) with the real len, writing way past the validated slot into adjacent read-only structures like ucred, task_t, and AMFI state.

The Patch: Two Instructions

Apple's fix in macOS 26.5 adds a per-CPU bound check. The diff:

-    cmp   x8, x29              ; stack-frame sanity check (useless)
-    b.lo  skip_stack_check
-    ; 6 instructions of alignment-mask arithmetic
-    adds  x9, x8, x4           ; target + len, runs LATE
-    b.hs  range_check
+    mrs   x10, TPIDR_EL1       ; per-CPU pointer
+    adds  x9, x8, x4           ; target + len, runs FIRST
+    b.hs  per_cpu_check
+    ldr   x11, [x10, #0x158]   ; per-CPU bound marker
+    ldr   x10, [x10, #0xe8]    ; per-CPU RO subzone base
+    cmp   x8, x11
+    ccmp  x9, x11, #0x0, hs    ; NEW: target+len vs per-CPU lower
+    ccmp  x9, x10, #0x2, hs    ; NEW: target+len vs per-CPU upper
+    b.ls  panic

The overflow check adds now executes first (before any wrap-handling), and the new per-CPU bounds ensure that target + len falls within the actual allocated slot, not just the RO zone range.

Memory Layout and Exploitability

MIE's read-only zone is page-table-protected—even ring-0 code can't write to it except through _zalloc_ro_mut. But once you control that function, you can overwrite adjacent ucred structures. Changing cr_uid from 501 to 0 gives root. The exploit by Calif.io used only public syscalls and took five days from zero bugs to root shell, assisted by Anthropic's Mythos Preview model.

Why This Matters

70% of high-severity vulnerabilities are memory-safety bugs (Google Project Zero, Microsoft). MIE was Apple's answer: hardware-level protection that makes bugs unexploitable. This bypass shows that even the best hardware defense can be undone by a single software bug. The two-instruction fix is a reminder that correctness starts at the code level, not just the silicon.

Key Takeaways for Developers

  • Always check for integer overflow before using the result. The wrap-handling path used the already-wrapped value.
  • Per-CPU bounds are a powerful defense. The fix uses TPIDR_EL1 to store per-core limits, preventing cross-core corruption.
  • Hardware security is not a silver bullet. MIE raises the bar, but attackers will target the remaining software interfaces.

Next Steps

Review any bounds-checking code that computes ptr + len for potential integer overflow. If you work on kernel or driver code, test with fuzzers that generate extreme sizes. Apple's patch is a case study in how a single arithmetic mistake can defeat a multi-year hardware security investment.