- New kern.ipc.lifecycle module: dead-name registration, dead-name send, notification receive, name reuse — single-threaded, safe on device - Device receipt (iPhone19,7, iOS 27.0.1, ad-hoc signed): request DEAD_NAME returns -300 (MIG_TYPE_ERROR); send -> MACH_SEND_INVALID_DEST; receive times out; 0/12,288 registrations armed in kern.ipc - macOS 27 sweep: 13 notification-type/right combinations, all -300 - kern.ipc now counts armed registrations and retracts the old 'survived' verdict when armed == 0; the request table is never armed - docs/KERNEL-RW-ASSESSMENT.md: 112507 marked closed for this app - app 2.2 build 5; 57 modules
9.1 KiB
Kernel read/write feasibility — high and critical dossiers
Scope: the kernel-reachable entries rated High or Critical in the Cinder27 dossiers. Question: can any of them be turned into arbitrary kernel read/write that does not panic the device?
Short answer: no, not from the material as it stands. One entry gives a real kernel heap leak (read), one gives a bounded read model, one gives a race receipt on a UAF-shaped bug. The write entries either write a constant at a fixed offset or never reach the kernel interface at all. This document records what each primitive actually provides, what an arbitrary-R/W chain still needs, and what was added to the POCs.
Guardrails
- The host Mac was panicked once by the CVE-2026-112705 encode path. That path is
never executed on the host again; its C twin refuses to submit a frame without
--force-host. - No blind kernel writes are attempted from any POC. A wrong guess in the kernel heap is a panic, not a data point.
- The simulator has no AppleAVE2 hardware encoder and no ANE kernel interface, so the write primitives cannot be exercised there either. Their app modules are split into a safe probe and a deliberate fire module.
Primitive inventory
| CVE | Entry | Primitive as shipped | Read | Write | Kernel R/W verdict |
|---|---|---|---|---|---|
| 2026-112705 | AppleAVE2 NumberOfTemporalLayers |
fixed-offset constant fill in the driver | no | fixed value, fixed offset | DoS only. No address control, no value control. The original write-up says explicitly: no arbitrary read/write capability. |
| 2026-65385 | IOKit IOHIDEventAppendEvent |
fixed-offset write into a short heap allocation | no | bounded, offset fixed | Not reachable as a primitive here. The POC stops at symbol resolution; the vendor-defined constructor is missing on the host. No leak, no reclaim, no gadget. |
| 2026-112696 | AppleH16ANEInterface | kernel interface reachability | no | none observed | No primitive. The ANE kernel symbols are absent for a third-party process; the module is an honest negative. |
| 2026-112507 | XNU ipc_port.c request-table race |
stale index → UAF-shaped race | no | none direct | Not reachable from the app. Device receipt: 12,288 notification registrations, 0 armed, 12,288 refused with -300 (MIG_TYPE_ERROR). No request-table entry exists, so there is no stale index to consume. |
| 2026-113910 | decmpfs chunked zlib | under-produced chunks expose uninitialised kernel heap through a file read | yes (bounded) | no | Leak only. Useful as a KASLR step; cannot write. |
| 2026-112554 | GPT partition-name scan | 72-B scan over a 28-B field | yes (bounded, model) | no | Leak only. The C twin models the width; the real read runs in the storage kext. |
What arbitrary kernel R/W actually requires
Every iOS kernel R/W chain needs five independent capabilities. A bug that supplies one of them is a chain step, not a chain.
- KASLR defeat — a kernel text or heap pointer with a known relationship to the unslid base. 113910 and 112554 can supply candidates; neither guarantees a text pointer, and a heap pointer alone does not give the slide.
- Heap grooming — deterministic placement of a victim object next to the corrupted allocation, in the same zone, at the same size class.
- Controlled reclaim / corruption — for a UAF: free the object, reallocate the slot with attacker-influenced contents, and keep a stale reference alive. For an OOB write: a value and an offset that land on a field worth corrupting.
- A read/write gadget — an object whose corrupted field yields a load or store
of an attacker-chosen address (e.g. a length+pointer pair, a page-table entry, a
thread's
pcbpointer). - PAC / PPL bypass — kernel code pointers are PAC-signed; page tables are protected by PPL on modern devices. A data-only path can sometimes avoid both, but it still needs 1–4.
None of the six entries supplies 2–5. 112507 is the only one that could plausibly reach step 3, and reaching it requires a device, kernel symbols, and a reclaim strategy that the repository does not have.
Per-entry notes
CVE-2026-112705 — fixed constant, fixed offset
The driver indexes a layer table with the declared count and writes a constant at a fixed offset past it. There is no attacker-chosen value and no attacker-chosen address. The only observable is a kernel data abort. Calling this "R/W" would require a second bug that turns the corrupted field into a pointer; nothing in the dossier provides one. The correct classification is a reliable kernel DoS.
CVE-2026-112507 — reachability closed on the device (2026-10-31)
The staged lifecycle harness was run on the target device (iPhone19,7, iOS 27.0.1)
with an ad-hoc signed build, and the race module was re-run with an armed counter:
mach_port_request_notification(DEAD_NAME)→ -300 (MIG_TYPE_ERROR),prev = 0.- Send to the dead name →
MACH_SEND_INVALID_DEST; receive →MACH_RCV_TIMED_OUT. kern.ipc: 12,288 registration attempts, 0 armed, 12,288 refused.- On macOS 27 the same call was swept over 13 combinations of notification type (SEND_POSSIBLE / PORT_DESTROYED / NO_SENDERS / DEAD_NAME) and right layout (receive-only, send+receive, send-only; notify port receive or send). Every combination returned -300.
The kernel refuses to arm a request-table entry for this process. With no entry,
the stale-index consumers in ipc_port.c have nothing to consume: the race cannot
be driven from an unentitled third-party app, and the "survived" line from the old
harness was measuring port churn, not the bug. The published fix shape (NULL checks
on ipr / table) is consistent with a denial or null-dereference path, not with a
reclaimable object.
Verdict: closed for this app. The remaining hypothetical is a platform process that is allowed to register notifications; that is outside Cinder27's reach.
CVE-2026-112507 — the race itself
The ring allocates receive rights, arms two notification kinds per port, tears down half the ring, then sends to the survivors. The request-table index consumers are the target of the race. A weaponised version would need:
- a win condition that is observable without panicking (a receipt, not a crash);
- a same-zone reclaim object with controllable contents (Mach ports themselves are one candidate, but the request-table entry's layout is not attacker-shaped);
- a stale consumer that dereferences the reclaimed slot after the free;
- a leak to place the reclaim object and to find the kernel base.
The POC records -300 and invalid destination receipts and reports that the
interleaving survived. That is the honest limit of a userspace race harness: it can
show the window exists, not that the window is controllable.
CVE-2026-113910 — the leak that was worth improving
This is the only entry where more code produces more signal. The C twin now:
- sweeps 8 chunk configurations (including
produce=0, which promises 64 KiB per chunk and writes none of it); - repeats the sweep
--loop Ntimes; - classifies every 8-byte-aligned value as kernel-text (
0xfffffff0…), kernel-data (0xffffffe0…), kernel-other, or user; - writes the raw readback to
--keep out.binfor offline analysis; - never dereferences a candidate.
On the macOS host decmpfs does not engage for a file that already has data (the documented oracle), so the run reports zero candidates and 3.2 MB of plain 0x41 readback. On an iOS device where the chunked xattr is honoured, the same sweep is the KASLR step. It still does not write.
CVE-2026-112554 — a read-width model, not a kernel reader
The C twin builds the overlong-name GPT and walks the name field the way 27.0 does. It proves the scan width (72 B against a 28-B field) but the bytes come from the image, not from the kernel heap. Treat it as a seed/oracle model. The real read would have to run through the storage kext on a mounted image; that is not attempted on the host.
What was deliberately not done
- No attempt to fire 112705 or 65385 on the host: both can panic the kernel, and a panic is not evidence of control.
- No blind write into a guessed kernel address: with KASLR and PAC in play, a wrong guess is indistinguishable from a bug that does not work.
- No claim that 112507 is exploitable: a race receipt is not a UAF primitive until the free/reclaim/stale-dereference sequence is observed.
Bottom line
Arbitrary kernel R/W without a panic is not achievable from these six entries as shipped, and the one entry that had a theoretical UAF path (112507) is now closed by device evidence: notification registration is refused, so the request table is never armed. The maximum safe capability the lab demonstrates is:
- a repeatable kernel heap leak with pointer classification (113910);
- a read-width model for the GPT scan (112554);
- a race-window harness with receipts (112507);
- honest negatives for the write entries that never reach a controllable primitive.
The next real step is not more code in this repository; it is a device session with kernel symbols, a reclaim strategy for 112507, and a stop condition for every write.