MIE / MTE -- Memory Integrity Enforcement / Memory Tagging Extension
MTE is Appleβs newest and most powerful hardware mitigation, deployed starting with iPhone 17 (A19) under the name MIE (Memory Integrity Enforcement). MTE detects heap corruption (UAF, overflow, type confusion) at the hardware level β turning most memory corruption bugs into crashes instead of exploitable primitives.
Reference: βNavigating the MTE Landscape: iOS Memory Protection Deep Diveβ β Atlan Pinabel & Patrick Ventuzelo (FuzzingLabs), OffensiveCon 2026.
Why MTE Is a Game-Changer
Before MTE:
UAF bug β reallocate freed memory β type confusion β kread/kwrite
Heap overflow β corrupt adjacent object β controlled primitive
β Most kernel exploits relied on memory corruption
After MTE:
UAF bug β pointer tag β memory tag β FAULT (crash, not exploitable)
Heap overflow β cross boundary β different tag β FAULT
β Memory corruption bugs become VERY HARD to exploit
ARM MTE Basics (ARMv8.5, 2019)
Concept
MTE attaches a 4-bit tag to every 16-byte granule of physical memory:
Pointer: 0x 0A 00FFFFF1234000
^^
Tag (4 bits, stored in bits [59:56])
Memory granule: Each 16 bytes has its own tag
Stored in "tag storage" (~3% of DRAM)
Each memory access:
Hardware compares: pointer tag vs memory tag
Match β access allowed
Mismatch β TAG CHECK FAULT (sync or async)
Tag Storage
Physical memory layout:
Low PA TagOffset_EL2 High PA
βββββββββββββββ¬βββββββββββββββββββββββββ¬βββββββββββββββββββ
β PHYS_SLIDE β AVAILABLE β TAG STORAGE β
β Firmwares β (covered by XNU β (1/32 of DRAM) β
β (SEP, TXM, β vm_pages) β β
β SPTM, KC) β β β
βββββββββββββββ΄βββββββββββββββββββββββββ΄βββββββββββββββββββ
Tag storage = ~3% of DRAM (1 tag = 4 bits per 16-byte granule)
Tag storage protected by SPTM (typed as XNU_TAG_STORAGE)
MTE Instructions
| Instruction | Function |
|---|---|
IRG Xd, Xn |
Insert Random tag Generation β generate a random tag |
GMI Xd, Xn, Xm |
Tag Mask β exclude specific tags |
STG [Xn] |
Store Allocation Tag β write tag to tag storage |
LDG Xd, [Xn] |
Load Allocation Tag β read tag from tag storage |
ADDG Xd, Xn, #imm, #tag |
Add with tag manipulation |
SUBG Xd, Xn, #imm, #tag |
Subtract with tag manipulation |
15 Usable Tag Values
4 bits β 16 possible values (0x0 - 0xF)
Tags 0x0 and 0xF are canonical (untagged)
β 15 usable values: {1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E} + one of {0, F}
Adjacent allocations get DIFFERENT tags
β Overflow into the next allocation = tag mismatch = FAULT
EMTE β Enhanced MTE (ARMv8.9, 2022)
FEAT_MTE4 extends MTE with:
- Canonical tag checking: tagged pointers cannot access untagged memory
- Tag permission: deny
STG/LDGthrough untagged PTEs - Checked Pointer Arithmetic (FEAT_CPA, ARMv9.5): detects pointer arithmetic bugs
Appleβs MIE (Memory Integrity Enforcement)
Deployment
- iPhone 17 (A19): First device with MTE/MIE
- M5 Macs: EMTE in synchronous mode only
- Integrated into both kernel allocators and userland allocators
4 Components of MIE
Apple's MIE (Memory Integrity Enforcement)
β
βββ 1. (E)MTE hardware β ARM instruction set
β + Tag Confidentiality
β
βββ 2. XNU integration β Kernel-level MTE management
β
βββ 3. Kernel allocator β zalloc/kalloc MTE tagging
β
βββ 4. User allocator β libmalloc/libpas MTE support
Tag Confidentiality Enforcement
Apple places special emphasis on tag confidentiality β preventing attackers from learning tag values:
| Protection | Mechanism |
|---|---|
| PRNG reseeding | Tag generation PRNG reseeded at every context switch (PACGA_IRG_RESEED macro) |
| Tag storage protection | Tag storage pages typed XNU_TAG_STORAGE, protected by SPTM |
| Side-channel resistance | SoC designed to resist TikTag, StickyTag, Spectre V1 timing attacks |
XNU Integration
MTE Activation β Per-Process Decision
MTE is decided per-process at exec time in imgact_setup_sec():
Inheritance enabled?
βββ YES β Enable MTE (mirror parent state)
βββ NO
posix_spawn explicit enable?
βββ YES β Enable MTE (through posix_spawn flags)
βββ NO
hardened-process entitlement?
(Or platform && DriverKit)
βββ YES β Enable MTE (through entitlements)
βββ NO
AMFI opt-out MTE?
βββ β Disable MTE
Entitlements Controlling MTE
| Entitlement | Effect |
|---|---|
com.apple.security.hardened-process |
Enable MTE (platform binaries only) |
com.apple.security.hardened-process.checked-allocations |
Enable MTE explicitly |
...checked-allocations.soft-mode |
βSoftβ mode = non-fatal faults. Ignored for platform binaries in lockdown. Forced for third-party (until iOS 26.4) |
...checked-allocations.{enable,disable}-pure-data |
Tag data-only allocations too (opt-out since iOS 26.4) |
Task Properties
// When activating MTE, XNU sets:
task->security_config.sec = true;
task->task_sec_policy = MTE_CONFIGURATION;
// Inject boot arg: has_sec_transition=1
// Check MTE status:
task_has_sec_*() // Family of check functions
VM Layer Integration (Userland)
MTE-capable task β allowed to use VM_FLAGS_MTE when requesting memory
β MTE enforced at every level in XNU VM structures
β Associated PTE indicates page can be tagged
β MTE-capable memory allocator uses tagged pages
Limitations:
- No CoW (Copy-on-Write) for tagged memory
- Inheritance / OOL mach message β force copy, tags stripped
Impact on exploitation:
- OOL mach message spray β tags stripped on copy β sprayed data is untagged
- CoW-based PUAF techniques (smith) β CoW disabled for tagged memory
- Cross-process memory sharing β tags not inherited
VM Layer Integration (Kernel)
| Layer | API | Role |
|---|---|---|
| VM | vm_page_grab*() |
Physical page allocation, can provide tagged page (VM_PAGE_GRAB_MTE) |
| VM | vm_page_alloc_list() |
Batch allocation, can provide tagged page (KMA_TAG) |
| kmem | kmem_alloc*() |
Huge wired allocations (page-granular), can provide tagged page (KMA_TAG) |
VM_PAGE_GRAB_MTE / KMA_TAG are only used through exclaves_memory_alloc, kalloc_large, and Zalloc APIs.
Kernel Allocators β Zalloc MTE Integration
XNU Heap Architecture with MTE
| Layer | API | Role |
|---|---|---|
| zalloc | zalloc(), zalloc_flags() |
Fixed-sized zones (SLAB, up to 32KB), enforcing MTE policy |
| kalloc / kalloc_type | kalloc_ext(), kalloc_type(T) |
Generic subsystem allocations, type segregation |
| IOKit | IOMalloc() |
Wraps kheap_alloc β kalloc_ext |
Existing Zalloc Defenses (Pre-MTE)
| Defense | Targets |
|---|---|
| External bitmap | No inline metadata to corrupt, double-free detection |
| Heap separation (DATA / PTR / SHARED) | Cross-type exploitation |
| VA sequestering | Cross-zone attacks (VA pinned to zone forever) |
| Sad Feng Shui / Per-CPU anti-LIFO magazines | Predictable heap feng shui |
| zone_require() | Validates zone ownership at runtime |
| Guard pages (random ~25%) | Linear overflows between zone chunks |
| Type segregation (kalloc_type) | Type confusion via heap spraying |
MTE Tagging in Zalloc
Tagging activation:
z_tag bit in zone's zone_security_flags_t (defaults to 1, except RO / DATA)
β Forces zone to init/expand via KMA_TAG and tag blocks
Since iOS 26.4:
z_tag replaced by runtime zone_submap_has_tagging_enabled
β Still avoid tagging for RO submap
β CAN enable tagging for DATA submap
β KHEAP_ID_DATA_BUFFERS split into:
KHEAP_ID_DATA_PRIVATE (tagged)
KHEAP_ID_DATA_SHARED (not tagged)
Tag-on-Free Policy
Lifecycle:
1. Zone init/expansion (zcram_memtag_init):
β New page grabbed β zcram_memtag_init() tags ALL blocks
β Even/odd position β different tag spaces
β Even blocks: tag space {2, 4, 6, 8, A, C, E}
β Odd blocks: tag space {1, 3, 5, 7, 9, B, D}
β Adjacent blocks ALWAYS have different parity tags
2. Allocation (zalloc):
β Block already tagged from init β vm_memtag_load_tag()
β Returns pointer WITH embedded tag
β Ready to use immediately
3. Free (zfree):
β Block position determined (odd/even)
β RETAG with new random tag from OPPOSITE parity
β Excludes current tag β guarantee different tag
β Old pointer's tag will NOT match β UAF detected!
XNU Source Code
// zcram_memtag_init β tag blocks when zone expands
static inline void zcram_memtag_init(zone_t zone,
vm_offset_t base, uint32_t start, uint32_t end)
{
if (!zone_submap_has_tagging_enabled(zone_security_config(zone))) { return; }
vm_size_t elem_size = zone_elem_outer_size(zone);
vm_size_t oob_offs = zone_elem_outer_offs(zone);
for (uint32_t i = start; i < end; i++) {
vm_offset_t addr = base + oob_offs + i * elem_size;
#if HAS_MTE
addr = (vm_offset_t)mte_generate_and_store_tag((caddr_t)addr,
elem_size, zone_mte_exclusion_mask((zone->z_chunk_elems - i) & 1));
#endif
}
}
Why MTE Makes Exploitation Extremely Difficult
UAF Detection
1. c1 = zalloc(zone) β c1 tagged with tag T1
2. zfree(c1) β memory RETAGGED with T2 (β T1)
3. *c1 β pointer tag = T1, memory tag = T2
β MISMATCH β FAULT
β UAF DETECTED!
If c1 is freed and reallocated to c2:
4. c2 = zalloc(zone) β c2 gets NEW tag T3 (β T1, β T2)
5. *c1 β pointer tag T1 β memory tag T3
β FAULT β Object confusion DETECTED!
Linear Overflow Detection
Adjacent blocks have different parity tags:
Block 0 (even): tag from {2, 4, 6, 8, A, C, E}
Block 1 (odd): tag from {1, 3, 5, 7, 9, B, D}
Overflow from block 0 β block 1:
β Pointer tag (even) β memory tag (odd)
β ALWAYS caught (different parity = guaranteed mismatch)
OOB to Same-Parity Block
OOB into a same-parity block (skip 1 block):
β Tag space overlap β 1/7 chance of matching
β NOT always caught, but probabilistic detection
Impact on Exploitation
Pre-MTE vs Post-MTE Attack Strategies
Pre-MTE (zalloc hardening + kalloc_type already made it hard):
β Layout difficult to predict (anti-LIFO, random magazines)
β Overflow/OOB might face guard pages
β Type confusion almost impossible (kalloc_type)
β Double free detected (external bitmap)
β BUT: UAF β same-type Object Confusion still possible
(mach ports, memory descriptors, etc.)
Post-MTE:
β UAF β Object Confusion: TAGGED, different tag β FAULT
β Overflow: adjacent = different parity β FAULT
β Linear overflow: always caught
β MOST heap exploitation techniques DEAD
What Attackers Can Still Do
MTE forces attackers to change strategy:
| Strategy | Viability |
|---|---|
| Attack untagged allocations (DATA-only, huge allocations) | iOS 26.4 closed the DATA gap |
| Intra-object primitives (corrupt fields WITHIN same object, same tag) | Possible β then potentially leak tags |
| Physical-level operations (DarkSword-style VFS race β physrw) | Bypasses MTE entirely (physical layer has no tags) |
| Hunt for other bug classes (logic bugs, parser disagreements) | Not affected by MTE |
| Tag leaking via side channels | Apple designed SoC to be resistant, but active research area |
Userland Allocators
- libmalloc: MTE support integrated
- libpas (WebKitβs allocator): now supporting MTE, but temporarily forced to soft-mode
- Soft-mode = non-fatal faults (log but donβt crash) β easier transition, less protection
- Platform binaries in lockdown mode: soft-mode ignored (hard faults enforced)
- Third-party apps: forced soft-mode until iOS 26.4
MTE in the Mitigation Timeline
iOS 6: KASLR
iOS 9: KPP (software)
iOS 10: KTRR (hardware kernel text protection)
PAN (Privileged Access Never)
iOS 12: PAC (Pointer Authentication)
PPL (Page Protection Layer)
APRR/SPRR
iOS 13: zone_require
iOS 15: kalloc_type (type-isolated zones)
CTRR (Configurable Text Readonly)
iOS 17: SPTM + TXM (EL2 separation)
iOS 18: Exclaves
iOS 26: MIE/MTE (Memory Integrity Enforcement) β NEW: hardware memory tagging
Tag Confidentiality (PRNG reseed, SPTM-protected tag storage)
Combined: SPTM + TXM + Exclaves + MTE = most hardened OS kernel in existence
Resources
- βNavigating the MTE Landscape: iOS Memory Protection Deep Diveβ β Atlan Pinabel & Patrick Ventuzelo, FuzzingLabs, OffensiveCon 2026
- ARM MTE Specification (ARMv8.5-MemTag)
- Apple β Towards the Next Generation of XNU Memory Safety: kalloc_type
- XNU source:
osfmk/kern/zalloc.c(MTE integration),osfmk/arm64/pmap.c(tag storage) - Research on MTE bypasses: TikTag, StickyTag (speculative side channels)