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.





