From 09a02858f61212ebd1ca5b7d3257e80c50227815 Mon Sep 17 00:00:00 2001 From: JBJF Date: Sat, 31 Oct 2026 17:14:56 +0800 Subject: [PATCH] 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 --- README.md | 2 +- README.zh-CN.md | 2 +- docs/IOS27.2-KERNEL-DIFF.md | 66 ++++------------ docs/SPTM-MIE-27.2.md | 153 ++++++++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+), 51 deletions(-) create mode 100644 docs/SPTM-MIE-27.2.md diff --git a/README.md b/README.md index 2caa1db..21f7960 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ Kernel R/W feasibility for the high/critical entries is in [docs/KERNEL-RW-ASSES 112507 reachability was re-measured on the target (iPhone19,7, iOS 27.0.1) with the new `kern.ipc.lifecycle` module and an `armed` counter in `kern.ipc`: `mach_port_request_notification` returns -300 (MIG_TYPE_ERROR) for every type/right combination tried, 0 armed out of 12,288 registrations. The request table is never armed, so the race is not reachable from an unentitled app; the earlier "survived the interleaving" line is retracted. Details in [docs/KERNEL-RW-ASSESSMENT.md](docs/KERNEL-RW-ASSESSMENT.md). -**iOS 27.2 firmware diff (27.0.1 vs 27.2, iPhone19,7):** [docs/IOS27.2-KERNEL-DIFF.md](docs/IOS27.2-KERNEL-DIFF.md). The update carries 162 advisory CVEs; 93 of 303 common kexts changed text size. Confirmed fix sites: 112705 clamps `NumberOfTemporalLayers` to 0…7 (`iNumberOfTemporalLayers <= 7` assertion), 113910 adds `chunk %u produced %lld of %lld expected bytes`, 112507 adds `ipr != NULL` / `table != NULL` in `ipc_misc.c`. `__DATA_SPTM` is byte-identical across both builds; the PPL/SPTM/MIE string and code surface is unchanged, so no bypass for those three is fixed in 27.2. +**iOS 27.2 firmware diff (27.0.1 vs 27.2, iPhone19,7):** [docs/IOS27.2-KERNEL-DIFF.md](docs/IOS27.2-KERNEL-DIFF.md). The update carries 162 advisory CVEs; 93 of 303 common kexts changed text size. Confirmed fix sites: 112705 clamps `NumberOfTemporalLayers` to 0…7 (`iNumberOfTemporalLayers <= 7` assertion), 113910 adds `chunk %u produced %lld of %lld expected bytes`, 112507 adds `ipr != NULL` / `table != NULL` in `ipc_misc.c`. The kernel's `__DATA_SPTM` segment is byte-identical, but the separate `sptm.t8160` firmware changed from SPTM-820.0.22 to **820.40.24** (+64 KiB: DART/IOMMU, TLBI and hibernation hardening, not in the CVE list). MIE/MTE policy in SPTM/XNU is unchanged. See [docs/SPTM-MIE-27.2.md](docs/SPTM-MIE-27.2.md) for the deep dive and candidate bypass directions. ## Module semantics diff --git a/README.zh-CN.md b/README.zh-CN.md index e5d1fb0..9b3a231 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -144,7 +144,7 @@ armed 不是超时——是**死亡 oracle 命中**:负载触发进程崩溃 112507 的可达性已在目标机(iPhone19,7 / iOS 27.0.1)上用新增的 `kern.ipc.lifecycle` 模块与 `kern.ipc` 的 `armed` 计数重测:`mach_port_request_notification` 在所有类型/权限组合上都返回 -300(MIG_TYPE_ERROR),12,288 次注册 0 次成功。请求表从未被武装,因此无特权 app 触达不到该竞态;此前“活过交错”的结论撤回。详见 [docs/KERNEL-RW-ASSESSMENT.md](docs/KERNEL-RW-ASSESSMENT.md)。 -**iOS 27.2 固件 diff(27.0.1 vs 27.2,iPhone19,7):** [docs/IOS27.2-KERNEL-DIFF.md](docs/IOS27.2-KERNEL-DIFF.md)。本次更新含 162 条公告 CVE;303 个共有 kext 中 93 个 `__TEXT_EXEC` 尺寸变化。已确认的修复点:112705 把 `NumberOfTemporalLayers` 收紧到 0…7(断言 `iNumberOfTemporalLayers <= 7`),113910 新增 `chunk %u produced %lld of %lld expected bytes`,112507 在 `ipc_misc.c` 新增 `ipr != NULL` / `table != NULL`。`__DATA_SPTM` 两版字节完全一致,PPL/SPTM/MIE 的字符串与代码面没有变化 —— 27.2 没有修这三个 bypass。 +**iOS 27.2 固件 diff(27.0.1 vs 27.2,iPhone19,7):** [docs/IOS27.2-KERNEL-DIFF.md](docs/IOS27.2-KERNEL-DIFF.md)。本次更新含 162 条公告 CVE;303 个共有 kext 中 93 个 `__TEXT_EXEC` 尺寸变化。已确认的修复点:112705 把 `NumberOfTemporalLayers` 收紧到 0…7(断言 `iNumberOfTemporalLayers <= 7`),113910 新增 `chunk %u produced %lld of %lld expected bytes`,112507 在 `ipc_misc.c` 新增 `ipr != NULL` / `table != NULL`。kernel 里的 `__DATA_SPTM` 段两版字节一致,但独立的 `sptm.t8160` 固件从 SPTM-820.0.22 升到 **820.40.24**(+64 KiB:DART/IOMMU、TLBI 与休眠加固,不在 CVE 列表里)。SPTM/XNU 的 MIE/MTE 策略没有变化。深度分析与候选 bypass 方向见 [docs/SPTM-MIE-27.2.md](docs/SPTM-MIE-27.2.md)。 ## 模块语义 diff --git a/docs/IOS27.2-KERNEL-DIFF.md b/docs/IOS27.2-KERNEL-DIFF.md index 3f93965..20cb021 100644 --- a/docs/IOS27.2-KERNEL-DIFF.md +++ b/docs/IOS27.2-KERNEL-DIFF.md @@ -126,57 +126,25 @@ per-CVE signature. ## PPL / SPTM / MIE -This is the part the request specifically asked to dig into. +**Correction:** the kernel's `__DATA_SPTM` segment is byte-identical (311,296 B, +same SHA), but SPTM is a separate firmware image and **did** change: -- `__DATA_SPTM` is **byte-identical** in both builds: 311,296 bytes, SHA-256 - `f4bc1e5e95f61af5…`. No SPTM code or data changed. -- The PPL token strings (`UAT_PPL_IOCTL_*`, `PPL map operation failed`, - `PPL state object`) are unchanged; no new PPL checks or messages. -- No MIE / Memory Integrity / mtag / page-protection strings were added or removed. -- The only SPTM-related additions are boot-time memory-size validation: - `memsize=%u MB leaves no memory for XNU/SPTM to manage`, `Mismatch of - ARM_LARGE_MEMORY in SPTM/XNU`, and iBoot DRAM-size checks. These are - configuration checks, not a permission bypass. -- The 27.2 advisory contains no component named PPL, SPTM, MIE, or Page Protection, - and no CVE description mentions them. +- `Firmware/sptm.t8160`: SPTM-820.0.22 → **SPTM-820.40.24**, 1,261,600 → 1,327,136 B + (+64 KiB, all in `__LATE_CONST`); `__TEXT_EXEC` same size, different hash. +- New checks: DART `VIOLATION_T8110_DART_UNGANG_GAPF_RACE`, GMMU TLBI + verification, IOMMU read-refcount validation, UAT Dekker lock timeouts, + `VIOLATION_NVME_ILLEGAL_HIBERNATION_QUEUE_LATCH`, hibernation handoff page count. +- `txm.iphoneos`: same size, build-path/version only (Image4 374~7022 → 374~8637). +- `exclavecore`: 33,259,754 → 33,489,130 B (+229,376) — separate domain. +- MIE/MTE policy in SPTM is unchanged: the `MTE tag page containing SK tags` + check is a 210-instruction function in both builds (relocated, same shape), and + the kernel's MTE strings are identical. The Exclave image changed separately. -**Verdict: no PPL, SPTM, or MIE bypass is fixed in iOS 27.2, and the firmware diff -shows no change in the SPTM/PPL/MIE code paths.** A bypass PoC for any of the three -would have to come from a different bug, not from this update. The kernel CVEs with -"write kernel memory" impacts are ordinary kernel memory-safety bugs; none of them -is a page-table / monitor bypass on its own, and exploiting one would still have to -defeat SPTM/PPL/MIE at runtime. - -## The 15 Kernel CVEs — what the advisory claims vs. what the binary shows - -| CVE | Advisory impact | Description | Binary evidence | R/W potential | -|---|---|---|---|---| -| 2026-112474 | terminate or **write kernel memory** | UAF | vm_map / lock-overflow strings (candidate) | theoretical UAF→R/W, no trigger | -| 2026-112476 | terminate | OOB read | kmalloc/zone paths, no unique string | read/DoS only | -| 2026-112501 | terminate | race | — | DoS | -| 2026-112507 | terminate or **write kernel memory** | OOB write | `ipr != NULL`, `table != NULL`, `ipc_misc.c` | **not reachable** from an unentitled app (-300) | -| 2026-112514 | terminate or corrupt kernel memory | UAF | framebank / page-lifecycle strings (candidate) | theoretical | -| 2026-112516 | terminate | UAF | — | DoS | -| 2026-112517 | terminate or corrupt kernel memory | UAF | — | theoretical | -| 2026-112576 | unexpected COW memory changes | memory integrity | `VM entry ... lock shared lock overflow` (candidate) | data-corruption primitive | -| 2026-112606 | terminate | type confusion | — | theoretical | -| 2026-112624 | bypass Privacy preferences | improved checks | new `com.apple.private.*` / TCC-adjacent strings (candidate) | privacy bypass, not kernel R/W | -| 2026-112665 | determine kernel memory layout | info disclosure | `size <= sdl->sdl_alen` (candidate) | KASLR leak candidate | -| 2026-112679 | terminate or corrupt kernel memory | UAF | — | theoretical | -| 2026-112698 | terminate | memory handling | — | DoS | -| 2026-65350 | terminate or corrupt kernel memory | UAF | — | theoretical | -| 2026-84495 | terminate or **write kernel memory** | UAF | — | theoretical | - -"Advisory impact" is Apple's wording. "Binary evidence" is what the 27.2 kernel -actually adds; where no unique string exists, the CVE cannot be pinned to a function -from the image alone. "Theoretical" means the primitive class can lead to R/W, not -that a working trigger is known. No trigger code for these 15 was recovered from the -diff, and no PoC was fabricated. - -Note the discrepancy on 112507: Apple calls it an out-of-bounds write, while the -27.2 fix is a NULL-check pair (`ipr`, `table`) plus a new `ipc_misc.c` string. A NULL -dereference is a DoS, not a reclaimable out-of-bounds object; this matches the -device result. +The full analysis, function addresses, and candidate bypass directions (DART +GAPF race, hibernation handoff, SK tag-page aliasing, dangling tag storage) are in +[SPTM-MIE-27.2.md](SPTM-MIE-27.2.md). No working SPTM/MIE bypass was produced: the +27.2 changes add checks, and the candidate paths need a reachable primitive that +the diff does not contain. ## What can be turned into a PoC diff --git a/docs/SPTM-MIE-27.2.md b/docs/SPTM-MIE-27.2.md new file mode 100644 index 0000000..71bfc01 --- /dev/null +++ b/docs/SPTM-MIE-27.2.md @@ -0,0 +1,153 @@ +# 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 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 +```