Files
cinder27/docs/SPTM-MIE-27.2.md
T
JBJF 09a02858f6 SPTM/MIE deep dive: sptm.t8160 820.22 -> 820.40.24 (+64 KiB)
- docs/SPTM-MIE-27.2.md: separate SPTM firmware changed (kernel __DATA_SPTM
  is not the monitor); new DART GAPF race, GMMU TLBI verification, iommu
  refcount, Dekker locks, hibernation handoff/latch checks; TXM build-only;
  Exclave +229 KB; MIE SK tag-page check unchanged (210 insns, relocated)
- candidate directions: DART unganged-GAPF race, hibernation handoff page
  count, SK tag-page aliasing, dangling tag storage; no working bypass
- correct the earlier claim that SPTM was unchanged
- tools: fw_diff, string_xref, func_string_diff
2026-10-31 17:14:56 +08:00

154 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPTM / MIE deep dive — iOS 27.2 (24B119) vs 27.0.1 (24A446)
This is the part that the kernelcache alone cannot answer. SPTM is a separate
firmware image in the IPSW (`Firmware/sptm.t8160.release.im4p`), and it **did**
change in 27.2. The earlier note that "SPTM is unchanged" was based only on the
kernel's `__DATA_SPTM` segment and was wrong: that segment is identical, but the
SPTM monitor itself is not.
## Component inventory
| Component | 27.0.1 | 27.2 | Change |
|---|---|---|---|
| `sptm.t8160` | 1,261,600 B · SPTM-820.0.22 | 1,327,136 B · SPTM-820.40.24 | **+65,536 B** |
| `txm.iphoneos` | 557,088 B · Image4 374~7022 | 557,088 B · Image4 374~8637 | build-only |
| `exclavecore.t8160` | 33,259,754 B | 33,489,130 B | +229,376 B |
| kernel `__DATA_SPTM` | 311,296 B · sha f4bc1e5e… | 311,296 B · sha f4bc1e5e… | **identical** |
| kernel MTE strings | `VM_KERN_MEMORY_MTAG`, taggable pages | same | unchanged |
SPTM segment sizes:
| Segment | 27.0.1 | 27.2 |
|---|---|---|
| `__TEXT_EXEC` | 425,984 | 425,984 |
| `__LATE_CONST` | 524,288 | **589,824** (+65,536) |
| `__TEXT` / `__DATA` / `__BOOTDATA` | 98,304 / 65,536 / 98,304 | same |
The +64 KiB is entirely in `__LATE_CONST`; the executable text kept its size but
its hash changed (`347aa218…` → `064c286f…`), so functions were edited/relocated.
## What 27.2 adds to SPTM
String diff (32 new, 7 removed) is dominated by IOMMU/DART and hibernation
hardening — none of it is named in the 162-CVE advisory:
- `VIOLATION_T8110_DART_UNGANG_GAPF_RACE`
- `%s: %s: iommu_read_refcnt (0x%hhx) > read_refcnt(0x%x) for FTE %p`
- `%s: Failed to verify TLBI`
- `%s: The GMMU TLBI verification register should be 8B-aligned`
- `gmmu-tlbi-verification-reg` / `gmmu-tlbi-verification-bit`
- `%s: UAT Dekker gate acquisition timed out after %llu ns`
- `%s: UAT Dekker lock acquisition timed out after %llu ns`
- `%s: dart %p (%s:%u): relaxed_rw_protections and allow_pte_remap are not supported together`
- `%s: dart %p (%s:%u): Inconsistent unganged clock protection configuration`
- `VIOLATION_NVME_ILLEGAL_HIBERNATION_QUEUE_LATCH`
- `hib_header_copy->handoffPageCount < HIB_HANDOFF_PAGECOUNT_LIMIT`
- `sptm_allow_vm_isa_internal_guests`, `sptm_is_iuou_or_iuos_device`
- `SPTM-820.40.24|2026-10-03:14:50:35`
The TLBI string has five code xrefs in 27.2, at functions starting near
`0xfffffff0270c7aac`, `0xfffffff0270c85f8`, `0xfffffff0270c987c`,
`0xfffffff0270cacf4` and `0xfffffff027109940`. These are new/edited DART
invalidation paths, not present in the 27.0.1 image.
The DART violation names are not referenced by a direct `adr`/`adrp+add` — they
are indexed through a violation table, so their exact raiser is a table lookup.
The name itself is the evidence that 27.2 adds a check for the T8110 DART
"ungang GAPF" race.
## MIE / MTE surface
MIE is not a single binary. The relevant pieces are:
- **SPTM**: manages tag pages and checks `MTE tag page containing SK tags is
mapped`. The function is at `0xfffffff0270c6320` in 27.0.1 and
`0xfffffff0270d7028` in 27.2; both are exactly 210 instructions, same prologue
and same range-check shape (`subs` + `ccmp` against two globals). It was
relocated, not rewritten.
- **Kernel**: the MTE page allocator (`VM_KERN_MEMORY_MTAG`, taggable pages, MTE
fill thread) has the same strings in both builds; no new MTE logic appears in
the 62 new kernel strings.
- **Exclave core**: carries the PMM tag machinery (`pmmtag_l4_decode`,
`Conclave MTE tag check fault`, shared-memory tags), and grew by 229,376 B.
That is a separate firmware update; no CVE in the advisory names it.
So the MIE *policy* surface in SPTM and XNU is unchanged in 27.2. The only
MIE-adjacent code that changed is inside Exclave, which is a separate domain.
Related SPTM strings that were already present in 27.0.1 and remain:
- `%s: Dangling tag storage reference from previous mapping attempt`
- `%s: Found SK-owned memory in non-wired memory`
- `%s: dart %p (%s:%u): power down not permitted as SK still using the DART`
The dangling-tag check is a real state machine: a previous tag-storage mapping
attempt can leave a reference that SPTM detects later. It exists in both builds,
so it is not a 27.2 fix; it is an audit target.
## Why Titan does not transfer
Titan (iOS 16.1–17.4b3, A14–A17) is not an SPTM logic bug. It uses:
1. AGX coprocessor code execution to overwrite the AGX `power` thread state;
2. the AGX hibernation context to restore a attacker-supplied `TTBR1_EL1` and a
second ROP stack (microPPL bypass);
3. a self-referencing page-table entry to map and write any physical page,
defeating PPL/SPTM.
Apple patched the AGX thread-state PAC in 18.1b4 and the TTBR restore in 17.4b4.
The 27.2 SPTM diff is not a return of that bug: it is DART/hibernation hardening
inside the monitor. A 27.2-era bypass would need a new primitive that can either
(a) make SPTM map a page table it does not protect, or (b) corrupt the tag/SK
metadata that SPTM trusts. There is no such primitive in the diff.
## Candidate bypass directions (hypotheses, not PoCs)
These are grounded in the 27.2 changes and the unchanged MIE checks. None is a
working exploit; each needs a reachable primitive that the firmware diff does
not supply.
1. **DART unganged-GAPF race.** 27.2 adds a violation for a race between
unganging a DART and a page-table remap. On 27.0.1 the race can leave a stale
DART translation if a mapping is unganged while a PTE is remapped. A stale
IOMMU translation is a DMA read/write primitive to memory SPTM considers
protected. Reachability: a userclient that owns DART mappings (IOGPU, display,
NVMe, PCIe). This is the closest analogue to a "SPTM bypass", but it is an
IOMMU bypass, not a page-table permission bypass.
2. **Hibernation handoff page count.** 27.2 adds
`hib_header_copy->handoffPageCount < HIB_HANDOFF_PAGECOUNT_LIMIT` and the
NVMe hibernation queue latch violation. On 27.0.1 a corrupted hibernation
header could make SPTM restore from a handoff page it did not size-check.
Reachability: modifying the hibernation image or the NVMe queue, not an app.
3. **SK tag-page aliasing.** The SPTM check only rejects a tag page whose
physical address falls in the `[start,end)` range loaded from two globals. A
mapping that aliases the same physical tag page at a different PA, or a
remap that changes the range after the check, would let user-controlled tags
cover SK memory. That is an MIE bypass: forge the tag for an SK page, then
read/write it. Reachability: a kernel primitive that maps tag storage with a
user-influenced PA; not present in the 27.2 diff.
4. **Dangling tag-storage reference.** SPTM already detects a dangling
reference from a previous mapping attempt. A race between unmap and remap of
tag storage could leave the stale reference usable; the freed tag page could
be reallocated as a user tag page. Same MIE impact as (3).
## Verdict
- SPTM **did** change: 820.0.22 → 820.40.24, +64 KiB, DART/TLBI/hibernation
hardening. The kernel-side `__DATA_SPTM` being identical is not evidence that
SPTM is unchanged.
- No SPTM/MIE bypass is named in the 27.2 advisory, and no page-table permission
check was removed or weakened in the diff. The changes add checks.
- MIE policy in SPTM/XNU is unchanged; Exclave changed separately.
- A working bypass was **not** produced. The candidate paths above need a
reachable primitive (DART userclient, tag-storage mapping, or hibernation
image control) and a device session; writing one blind would be guesswork.
## Reproduce
```bash
python3 tools/ipsw/remote_ipsw.py extract <url> sptm work/fw/27.2-sptm.im4p
python3 tools/ipsw/ipswlib.py kernel ... # IM4P/LZFSE decode
python3 tools/string_xref.py work/fw/27.2-sptm.bin "Failed to verify TLBI"
python3 tools/fw_diff.py work/fw/27.0.1-sptm.bin work/fw/27.2-sptm.bin
```