Skip to content
HN On Hacker News ↗

Pardon MIE? - ironPeak Blog

▲ 13 points by jiveturkey 11h ago HN discussion ↗

Pangram verdict · v3.3

We believe that this document is primarily AI-generated with some human-written content

92 %

AI likelihood · overall

AI
5% human-written 95% AI-generated
SEGMENTS · HUMAN 1 of 7
SEGMENTS · AI 6 of 7
WORD COUNT 1,642
PEAK AI % 100% · §1
Analyzed
May 23
backend: pangram/v3.3
Segments scanned
7 windows
avg 235 words each
Distribution
5 / 95%
human / AI fraction
Verdict
AI
Pangram v3.3

Article text · 1,642 words · 7 segments analyzed

Human AI-generated
§1 AI · 100%

Pardon MIE? Sat May 23, 2026Pardon MIE? Sat May 23, 2026Apple’s Memory Integrity Enforcement is no joke. Five years of design, brand-new M5 silicon, hardware memory tagging on the kernel heap, hardware-locked read-only zones for the kernel’s crown jewels, and a privileged monitor sitting above the kernel that refuses every unauthorised page-table change. It’s the most serious kernel memory-safety stack any consumer OS has shipped. And it still got bypassed. A three-person shop with an AI sidekick walked through it in five days, with two bugs and a clever idea. Here’s my rundown of how they achieved it, no PhD required.Skip ahead if you want:The bug, in 60 secondsThe fix, in two instructionsFor defendersFor exploit writersWhy memory safety mattersAlmost every “your iPhone got hacked” story you’ve ever read comes back to the same root cause: a memory bug. A pointer that pointed at the wrong thing. A buffer that wrote one byte too many. A struct that got reused after it was supposed to be freed.It’s not the user clicking a sketchy link. It’s not a stolen password. It’s the kernel reading from a piece of memory and trusting it just a little too much.Pegasus, the NSO Group spyware that ended up on journalists and activists worldwide? Memory bug. The WebKit zero-clicks used in targeted operations all through the 2020s? Memory bugs. The checkm8 jailbreak that owned every iPhone from the 5s through the X? Memory bug. The T2 vulnerability I wrote about a few years ago? Same family.Google’s Project Zero has been counting these for years. Roughly 70% of high-severity vulnerabilities across all major software products are memory-safety bugs. Microsoft counted theirs and got the same number. Chrome did, Firefox did, Apple did. The whole industry agrees: this is the problem.So what does an attacker actually do with a memory bug?In the worst case, anything they want. The kernel is the program that runs every other program. If you can corrupt the kernel’s memory, you can rewrite the table that says “this process is allowed to read your photos”, or “this user is uid=501”.

§2 AI · 100%

You can install something that survives a reboot. You can read every message the device has ever sent. You can flip the camera on without the LED. The phone you bought to keep your life private is now someone else’s diary of your life.This is why Apple, Microsoft, Google and the chip vendors have been pouring engineering at hardware-level memory safety for a decade. Compiler hardening, safer allocators, sandboxing: they all help, but they’re a leaky bucket. The bugs keep coming. The thing that actually works is making the bugs unexploitable even when they exist. Stop trusting the software to be correct; build the hardware to refuse incorrect operations.That’s the bet MIE is making.Recap: what is MIE supposed to do?If you’ve followed Apple’s security marketing in the last year, you’ve heard MIE described as a “generational leap”. It’s three pieces, stacked.Memory tagging (EMTE). Every chunk of memory the kernel hands out gets a hidden label, a tag. The pointer you use to reach that chunk has the same tag baked into its upper bits, where it doesn’t change the address. On every access, the CPU checks: does the pointer’s tag match the memory’s tag? If not, your process dies on the spot. Apple’s version is synchronous, which means the check fires on the access itself, not later. You can’t guess tags by probing because the first wrong guess kills you.Read-only zones. Some kernel structures are so juicy they get extra protection. Things like ucred (your process’s user ID), the Mach task control block, the sandbox table, the codesigning state. They live in a special region where the page tables themselves mark the pages as unwritable. Even ring-0 kernel code can’t touch them. The hardware MMU refuses.One door in. Exactly one kernel function is allowed to mutate read-only-zone pages: _zalloc_ro_mut. It briefly marks a page writable, does its write, marks it unwritable again. A higher-privileged thing called the Secure Page Table Monitor watches every page-table change and refuses if anyone else tries. From the kernel’s own perspective, “only _zalloc_ro_mut writes here” is unbreakable.Stack those three together and you get MIE.

§3 AI · 100%

Most kernel memory is tag-protected. The crown jewels are page-table-protected behind one audited writer. Pretty good design, honestly.What just happened?On 11 May 2026 Apple shipped macOS Tahoe 26.5. Buried in the security notes was a one-liner:Kernel. Available for Mac computers with Apple Silicon. An app may be able to cause unexpected system termination. Credit: Calif.io in collaboration with Claude and Anthropic Research. CVE-2026-28952.“Unexpected system termination” sounds like a crash bug. It is not. Three days later Calif published their disclosure: the first public macOS kernel exploit on an M5 with MIE enabled. Unprivileged local user, only public syscalls, root shell. Five days from “no bugs in hand” to working exploit. They used Anthropic’s restricted Mythos Preview model throughout.Apple’s patch is two instructions long. Two. Those two instructions tell the whole story. Let me show you.If you read arm64 assembly, here’s the whole fix at a glance. We’ll unpack it properly below.--- com.apple.kernel @ macOS 26.4.1 :: _zalloc_ro_mut, bounds check +++ com.apple.kernel @ macOS 26.5 :: _zalloc_ro_mut, bounds check @@ argument validation @@ - 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

§4 AI · 100%

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 Three differences: a useless stack-overlap check is gone, the overflow check moved earlier, and a brand-new per-CPU bound was added. If that already makes sense to you, skim the rest. If not, read on.The bug, in 60 secondsThe whole thing lives in one kernel function: _zalloc_ro_mut. Here’s its C-level signature:void _zalloc_ro_mut(zone_id_t zone, // which RO zone void *target, // slot to write into size_t offset, // offset inside the slot const void *src, // bytes to copy in size_t len); // how many bytes Translation: “find this slot in this RO zone, and copy len bytes from src into target + offset”. Before doing anything, it has to bounds-check that the destination plus the copy size doesn’t run off the end of what’s allocated. The 26.4.1 check looked like this in pseudocode (I’m dropping the offset parameter for clarity; the real code adds it to target upfront):// pre-patch (26.4.1) uint64_t end = target + len; // wait for it... if (end < target) { // overflow detection // wrap-handling path, uses `end` (already wrapped!) if (target >= ro_zone_lo && end <= ro_zone_hi) goto write_ok; // 🤡 } Spotted it?

§5 AI · 100%

If len is huge enough that target + len wraps past 2^64, then end becomes a tiny number. The wrap-handling path still compares this tiny end against the RO zone range. A tiny number is comfortably below ro_zone_hi, so the check passes. The function happily calls memcpy(target, src, len) with the real len, which writes way past the validated slot into whatever lives next door.What lives next door in the RO zone? Other ucred structures. Other task_t blocks. AMFI state. Codesigning flags.

§6 Human · 12%

The exact stuff MIE was built to protect.That’s CVE-2026-28952. Integer overflow in a bounds check. Apple’s CVE wording, “addressed with improved input validation”, finally makes sense.Memory layout, because pictures helpQuick mental model. The kernel is just a program. Like any program, it allocates memory and stores things in it. Picture the kernel’s memory as a giant warehouse with different sections for different kinds of stuff. The general-purpose section, where most allocations land, is called the kalloc heap. The locked-down section for the high-value structures is the RO zone (read-only zone).Drawn out:high addresses ─────────────────────────────────────────────── │ │ ┌─────────────────────────────────────────────┐ │ │ kernel code & rodata │ │ └─────────────────────────────────────────────┘ │ ┌─────────────────────────────────────────────┐ │ │ general kalloc heap (EMTE tag-protected) │ ← classic │ │ │ UAF/OOB │ └─────────────────────────────────────────────┘ bugs land │ here │ ┌─────────────────────────────────────────────┐ │ │ RO allocator zones │ ← SPTM-locked │ │ ┌─────────┬─────────┬─────────┬─────────┐ │ read-only, │ │ │ ucred │ task_t │ amfi │ ... │ │ only │ │ └─────────┴─────────┴─────────┴─────────┘ │ _zalloc_ro_mut │ └─────────────────────────────────────────────┘

§7 AI · 100%

can write │ ┌─────────────────────────────────────────────┐ │ │ per-CPU data (TPIDR_EL1 points here) │ │ └─────────────────────────────────────────────┘ low addresses ──────────────────────────────────────────────── Three things to notice:The kalloc heap is where almost everything the kernel allocates lives. Network buffers, file descriptors, IPC messages, the works. Classic kernel exploits like use-after-free and out-of-bounds writes traditionally land here.The RO zone is the part Apple chose to lock down at the hardware level. The CPU’s page tables mark these pages unwritable for the whole kernel. Even if you fully compromise the kernel and get an arbitrary-write primitive, you still can’t touch these pages directly. There is exactly one exception: _zalloc_ro_mut.The per-CPU data at the bottom holds per-core bookkeeping. The TPIDR_EL1 register on ARM64 points to it. Remember this part. It matters for the fix later.Now zoom in on one slot in the RO zone. The structure we care about is ucred, the per-process “user credentials” struct. Whenever the kernel decides whether your process is allowed to do something, it checks the ucred. The interesting field is cr_uid, your effective user ID. cr_uid == 0 means root. cr_uid == 501 means me. Or you, probably.RO zone slots of the same type sit packed together, like cubicles in an open-plan office. Each ucred is roughly 200 bytes. Here’s what one looks like inside (from public XNU source, bsd/sys/ucred.h):struct ucred { os_refcnt_t cr_ref; struct posix_cred { uid_t cr_uid; // ← effective UID, the prize uid_t cr_ruid; // ← real UID uid_t