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/LDG through 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