Full Report
Memory corruption vulnerabilities are absolutely everywhere in modern exploit development. Something like 80% of bugs are memory corruption. This post goes into making the XNU (iOS & Mac backbone) OS memory allocator much harder to exploit via these types of vulnerabilities. XNU has several memory allocator APIs but there are two main subsystems: the zone allocator for small chunks and the VM allocator which has page granularity and permission tracking; this post is focused on the zone allocator hardening. This allocator is a generic slab allocator, where a collection of pages are divided into equal chunks of data. There can be a special zone for a given use case, like ipc ports, which is a collection of chunks that can even be subdivided. When under memory pressure, chunks from a given zone may be reclaimed. First, let's define memory safety in a few classes. Temporal safety is only using objects during their allocation timeline (UAF, double free). Spatial safety is only accessing memory that belongs to the particular chunk (buffer overflow, OOB read). Type safety is only using the object as the intended type (type confusion). Definite initialization is asserting that ALL allocations will be properly initialized (info disclosures). Finally, thread safety is ensuring concurrent access is done safely (race conditions). In most exploits, a tiny (constrained) vulnerability is used to make a stronger primitive to eventually hijack the control flow. The first goal to prevent exploitation is type isolation, which things like GigaCage in WebKit pioneered. Preventing the access to specific data structures makes exploitation much more difficult. The second goal is to prevent the trivial overwriting of pointers with data. By isolating each of these as much as possible from each other, a buffer overflow with a user controlled string can no longer cause too much havoc. The rest of the article is explaining how these two systems are implemented. An exploit UAF on iOS had the following flow: Allocate a bunch of objects then trigger a UAF on one of these to create a dangling reference. Free all of the objects for the target in step 1. This makes everything in the zone completely free. Create memory pressure that the zone containing the memory we want gets reused. Allocate a large number of objects, hopefully allocating over the memory from before. This creates a type confusion that can be easily exploited. Since the path above was so reliable, it is time to mess this up. The first item is to make virtual address space reuse across zones (step 3 from the memory pressure) not possible. This was done by allowing for the reuse of physical memory but NOT the virtual memory for single-type zones. Now, simple overwrites of objects like thread and proc can no longer be exploited in this way. The next step is to help isolate data from pointers. The first remediation step is introducing an allocation type that only contains data called KHEAP_DATA_BUFFERS that will live in its own section of memory. Secondly, a sized based collection of zones with a particular namespace of allocations. The final step to making exploitation harder is a non-deterministic allocator. Of course, every type of object cannot have its own zone because of memory constraints. However, the group of objects put into each zone can be randomly selected at boot time, making exploitation inconsistent. They choose 200 zones to divide into different groups depending on the size. For dynamically sized allocations, they disallowed the usage of a non-data header followed by a data-only type; this was to prevent trivially moving to the right zone for exploitation. Additionally, a number of heaps were exclusively created for these variable sized allocations. Finally, arrays of pointers had their own heap section as well. So, we've done a ton of work to harden the allocator. How does this stack up to other things? For type isolation, IsoHeap is perfect (no reuse of zones) but kalloc_type has a large amount of buckets that are randomized for each size. Additionally, the heap metadata for kalloc_type is completely in a different section of memory, unlike others that store freelist pointers inline. Overall, this is a great step forward for protecting the XNU kernel. With these mitigations making exploitation of heap issues harder and the presence of pointer authentication, XNU attacks will require extremely strong primitives from the beginning or a very sophisticated attacker.
Analysis Summary
# Research: Towards the Next Generation of XNU Memory Safety: kalloc_type
## Metadata
- **Authors**: Apple Security Engineering and Architecture (SEAR)
- **Institution**: Apple Inc.
- **Publication**: Apple Security Research Blog
- **Date**: October 27, 2022
## Abstract
This research details the architectural evolution of the XNU kernel memory allocator, specifically focusing on the introduction of `kalloc_type`. The initiative aims to mitigate the exploitability of memory corruption vulnerabilities—which account for roughly 80% of modern bugs—by hardening the zone allocator. Through techniques such as virtual memory sequestering, type-based isolation, and randomized bucketing, XNU aims to break the reliable "primitive-building" phase of exploit development, particularly for temporal safety issues like Use-After-Free (UAF).
## Research Objective
The primary objective is to make memory corruption exploits inherently unreliable and non-reusable. By hardening the allocator, Apple seeks to prevent attackers from using a "constrained" vulnerability (like a minor UAF) to build a "strong" primitive (like type confusion) that leads to arbitrary code execution.
## Methodology
### Approach
The research adopts a multi-layered defensive strategy:
1. **Type Isolation**: Separating different data structures into distinct memory regions to prevent cross-type reuse.
2. **Data/Pointer Separation**: Segregating pure data (buffers) from structures containing pointers to prevent trivial overwrites.
3. **Non-Deterministic Allocation**: Introducing entropy into the allocation process to ensure attackers cannot predict memory layout.
### Dataset/Environment
The study focuses on the **XNU Kernel**, the core of iOS, iPadOS, and macOS. The mitigations were incrementally rolled out across **iOS 14, 15, and 16**.
### Tools & Technologies
- **Zone Allocator**: The primary subsystem for small-chunk kernel allocations.
- **kalloc_type**: The new hardened allocator API.
- **Pointer Authentication (PAC)**: Hardware-level protection used in conjunction with allocator hardening.
## Key Findings
### Primary Results
1. **Elimination of Virtual Address Reuse**: By sequestering virtual memory for single-type zones, the system prevents an attacker from reclaiming a freed object's virtual address with a different object type, even if physical memory is reused.
2. **Architectural Separation of Heaps**: The introduction of `KHEAP_DATA_BUFFERS` ensures that user-controlled strings/data cannot overlap with sensitive kernel structures.
3. **Probabilistic Mitigation via Randomization**: By distributing allocations into ~200 randomized buckets based on type signatures at boot time, the reliability of heap spray attacks is significantly reduced.
### Novel Contributions
- **kalloc_type API**: A signature-based allocator that uses C++ type information to automatically route allocations to the appropriate isolated zone.
- **Dynamic Size Hardening**: Disallowing the mixing of headers and data in dynamic allocations to prevent zone-hopping.
## Technical Details
The mitigation addresses the standard UAF exploit flow (Allocate → Free → Memory Pressure → Reallocate with different type).
- **Virtual Memory Sequestering**: Even when the kernel is under memory pressure and reclaims physical pages, the virtual addresses associated with a "Single-Type Zone" are never reassigned to a different zone type.
- **Randomized Bucketing**: Rather than a 1:1 mapping of types to zones (which would exhaust memory), XNU hashes type signatures into one of many buckets. This "collapsing" of types is randomized at each boot, making a successful exploit on one device fail on another.
## Practical Implications
### For Security Practitioners
- Exploit chains now require significantly stronger initial primitives. A simple UAF no longer guarantees a type confusion; the attacker must find a way to influence the specific randomized bucket or find vulnerabilities within the same type-isolated zone.
### For Defenders
- **Hardened Defaults**: The allocator now zeros pointers upon `kfree_type()` calls, reducing the window for dangling pointer exploitation.
- **Reduced Attack Surface**: Moving `ipc_kmsg` (a common target) out of the data submap removes a major historical exploit path.
### For Researchers
- Research must shift toward **intra-zone exploitation** (finding vulnerabilities between objects of the same type) or finding methods to leak the boot-time randomization seeds.
## Limitations
- **Memory Constraints**: Not every single C struct can have a unique zone without causing memory exhaustion; thus, "bucket collapsing" is a necessary compromise.
- **Legacy Code**: While `kalloc_type` is widely adopted, older kernel extensions may still use legacy, less-secure `kalloc` APIs.
## Comparison to Prior Work
- **vs. IsoHeap (WebKit)**: While IsoHeap provides perfect isolation, it is too memory-intensive for the kernel. `kalloc_type` provides a middle ground using randomized buckets.
- **vs. Traditional Slab Allocators**: Unlike traditional allocators that store "freelist" pointers inline (vulnerable to overflows), `kalloc_type` moves metadata to a dedicated, sequestered memory section.
## Real-world Applications
- **iOS/macOS Kernel**: Directly implemented in the XNU core to protect sensitive structures like `thread` and `proc`.
- **High-Security Environments**: These mitigations make the cost of developing functional "zero-click" exploits substantially higher for sophisticated actors.
## Future Work
- **Signature Diversity**: Increasing the diversity of type signatures to further reduce the "collapsing" of distinct types into the same buckets.
- **Safe Languages**: Long-term transition of kernel components to Swift to provide memory safety at the language level rather than the allocator level.
## References
- Apple Security Blog: *Towards the next generation of XNU memory safety: kalloc_type*
- Related: *GigaCage (WebKit)*, *PAC (Pointer Authentication Codes)*.