macOS kernel heap (zone allocator)
TL;DR: XNU’s kernel heap is a per-type zone allocator — recent versions add
kalloc_typeisolation so an attacker can no longer reliably reclaim a freed object of type A with an attacker-controlled type B; defeating that means finding a confused-deputy across the same zone or akalloc_databuffer that touches the right pointers.
What it is
The XNU kernel uses a “zone allocator” — each commonly-allocated structure (ipc_port, task, vm_map_entry, OSData) gets its own zone with a fixed element size and pre-typed freelist. The classic kmem heap-spray that worked on older macOS (overflow into adjacent attacker-controlled buffer in the same generic kalloc bucket) is dead on modern XNU because:
- kalloc_type isolation (macOS 12+, iOS 15+): the compiler emits
kalloc_type(...)calls that route allocations to a zone keyed on the static type of the allocation site.OSData::initWithByteslands inOSDatazone; nothing else lives there. - Per-CPU caching layer (
zalloc_caching): small allocations come from a per-CPU magazine. - PGZ (Probabilistic Guard malloc): randomly samples allocations into a guarded zone with red-zone pages between them; catches OOB writes probabilistically.
- Zone freelist hardening: freelist pointers are XOR’d with a per-zone secret cookie, so a UAF-derived freelist write can’t trivially aim the next allocation.
Preconditions / where it applies
- macOS / iOS kernel heap exploit. Older XNU (pre-Big Sur) is much easier — most public CTF write-ups assume that environment.
- You already have a kernel UAF, double-free, or OOB-write primitive and need a reliable target object to corrupt.
Technique
Step 1 — Identify the zone. Use zprint (live, requires root) or static analysis (kalloc_type macro expansion in headers) to determine which zone the vulnerable object lives in. If it lives in a dedicated kalloc_type zone, you can only spray that exact type.
Step 2 — Find a useful zone-mate. The classic recipe: free the vulnerable object, reallocate the slot with an object of the same zone whose contents include a function pointer / vtable you can leak or hijack. Modern kalloc_type makes this hard — but:
- Many small heap-allocated buffers route to
kalloc_data(raw data, untyped). If your vulnerable object has akalloc_dataadjacency (sequential allocation), you can spraykalloc_datawith attacker-controlled bytes viaIOSurfaceproperty dictionaries, mach-message inline OOL ports, etc. OSObject-derived classes sometimes share zones if their static sizes match (zone-binning fallback).
Step 3 — Spray. Common kernel sprays:
- IOSurface OSData properties (
IOSurface_setValue) — attacker bytes inkalloc_datazones. Size-controlled. - Mach messages with inline OOL descriptors — port arrays land in the
ipc_kmsgzone but pointer descriptors give read/write primitives. pipe()buffer kernel-side allocations — Linux trick that partly applies (pipe pages live in vm_object, not zone).vm_map_entryflooding via mapping many regions; useful for adjacency on the VM map zone.
Step 4 — Bypass PGZ. PGZ samples ~1 in N — exact rate is platform-specific. Reliability strategy: allocate enough victim objects that the probability of all being PGZ-protected is negligible (e.g., 100 sprays).
Step 5 — Defeat the freelist cookie. You can’t forge a next-pointer without leaking the per-zone secret. Two paths:
- Leak the cookie via a related infoleak (sometimes one bug gives both primitives).
- Skip freelist corruption entirely — use a UAF where the same slot is allocated to an attacker-controlled type without needing to redirect the freelist.
Detection and defence
- Apple-side: PGZ + kalloc_type are the moving target; new XNU releases tighten the type universe.
- Researchers / exploiters: track XNU source releases (
xnu/osfmk/kern/zalloc.c) — kalloc_type rules and zone definitions change every major release. - Build kernel with
KASANfor development to catch heap bugs early.
References
- Apple — kalloc_type security strategy — official explainer
- XNU source —
xnu/osfmk/kern/zalloc.c— canonical implementation - macOS and iOS Internals, Volume II — Jonathan Levin; chapter on Mach zones
- Synacktiv — Exploiting XNU heap bugs in 2024 — modern primitives walkthrough