# ASD service-2 message handler: attack surface for a GCP-ROP -> ASD escalation Target of `fw/LEAD-GCP-ROP-TA-KERNEL-SURFACE.md`'s revised plan: a ROP'd GCP TA can send an arbitrary crafted `SVC 0xF2` message to **service 2 = ASD** (reachable: TA cap bit `0x80` is set; only service 3/SYS_DRV needs type 1). Code exec in ASD is a type-5 -> type-2 escalation that escapes the `<=0xDF` SVC jail (types 1/2 may issue the full surface incl. the `0x7C`/`0xA0` poke and MMIO), from which fuses become reachable. This note maps ASD's dispatcher so the bug hunt is resumable. Source: `asd_code.bin`, base `0xD00100` (two-point verified). ## Transport (already proven to work) GCP RPCs ASD over service 2 today for cmds `0x109/0x201/0x204/0x305` (see GCP `0x4061d0`: `stm sp,{cmd,arg..} ; movs r0,#2 ; svc 0xf2`). The message is a small struct in the TA's address space (the `0xF2` range check pins a type-5 caller's `msg` to `[0x400000,0x800000)`): msg[0x00] = cmd msg[0x04] = arg1 msg[0x08] = arg2 msg[0x0c] = arg3 ... From ROP we reuse that exact `svc 0xf2` path with any `cmd` and any args. ## Dispatcher `sub_D013B8` (0xD013B8): command groups cmd = *msg (r6). Range dispatch: 0x180..0x1C0 -> 0xd01464 tbh table, 28 distinct handlers <- biggest, GCP-unused 0x100..0x10D -> 0xd014f2 (GCP uses 0x108/0x109) 0x200..0x20E -> 0xd0151c (GCP uses 0x201/0x204) 0x300..0x309 -> 0xd01546 (GCP uses 0x305) 0xFFFF0000..7 -> 0xd01568 tbh 0x402..0x405 -> 0xd01588 tbh 0x160,0x161,0x800,0x801,0x802 -> individual else -> [msg+4] -> bl 0xd15bcc ## The 0x180 group (0xd01464): 28 handlers, each forwards message fields to a worker Examples (all read `msg+4..`): 0x182-0x185 ldm {r0,r1,r2} from msg+4 -> worker(arg1,arg2,arg3) (ptr/val/len shape) 0x186 r0=msg+4, r1=u16 msg+8, r2=u16 msg+0xa -> 0xd0f2ac (ptr + two u16 lengths) 0x18c ldm {r0,r1,r2,r3} from msg+4 -> 0xd0f960 (4 controlled args) 0x18e/0x1a7 ldrd msg+4, byte msg+0xc, word msg+0x10 -> worker (ptr64 + len/flags) ... (full list enumerated in the audit script output) Each worker begins with a capability gate `bl 0xd116ac` (r0 = a per-cmd capability id, e.g. 0xf). ## The gate `0xd116ac` is permissive in the unprovisioned state ldrb r2,[global] ; cbz r2 -> return 1 (ALLOW) ; if the ASD state byte is 0, EVERY cap passes else: switch(capid) -> load [global+off] ; return (slot != 0) So in ASD's default/unprovisioned state (the state GCP drives it in over the ring), the byte is plausibly 0 and the whole 0x180 group is **open**. Once ASD is provisioned the per-capability slots gate it. The runtime value of `[global]` in our scenario is the first thing to pin (emulator boots real ASD; `psp_emu` full_system loads ASD). ## The bug to find (not yet found) A 0x180-group (or 0x200/0x300/0x800-group) handler that, past the gate, copies a **message-controlled length** of bytes into a **fixed-size ASD buffer** (stack or global), or indexes an ASD table/function-pointer array with a **message-controlled index** beyond its bounds. The dispatcher already shows one indirect-call-through-table site (`0x186` -> `blx [table+0x1828]`), so an index/table-base bug there yields ASD PC control directly. Candidate workers to read next, for the (dst,src,len)/(ptr,len) copy shape: `0xd0f648, 0xd0f960, 0xd0f114, 0xd0fa1c` (the multi-arg ones). Pointer args are constrained to the TA window, but ASD-internal destination buffers are not, so a controlled length overflows ASD memory. ## Why this is the right target Code exec in ASD (type 2) is the pivot the whole session's GCP ROP work was building toward for kernel/fuse reach: type 2 is not in the `<=0xDF` SVC jail, so it can MMIO-read the FUSE/CCEFUSE blocks directly (and reach SYS_DRV's surface). The GCP->ASD transport is proven; the gate is plausibly open; the only missing piece is one memory-safety defect among ~40 reachable ASD handlers. Audit scripts: this map was produced by disassembling `asd_code.bin` at base 0xD00100; the 0x180 tbh table and worker heads are reproducible from the dispatcher at 0xD013B8. ## Audit pass 1: first-level 0x180 handlers validate (ptr,len), but only against a 256 MB cap Read the multi-arg workers `0xd0f648 (0x182)`, `0xd0f960 (0x18c)`, `0xd0f114 (0x18e)`, `0xd0f2ac (0x186)`. All share a shape and come back **bounded at this level**: 1. capability gate `bl 0xd116ac` (permissive while ASD unprovisioned, see above); 2. channel index arg1 (`msg+4`) bounded `<= 0x3e/0x3f`, then `table + arg1*0x60` -- no OOB index; 3. pointer/length args validated via `bl 0xd12e90` before use; 4. dispatch `blx [asd_table + 0x183c / 0x1828 / 0x1c84]` to a per-channel handler, or a fixed-size copy (`0x18e` copies a constant 5 bytes per element via `0xd01058`). So the first dispatch level is not trivially overflowable. **But the validator does not close the overflow class.** `0xd12e90(ptr,len)`: cmp ptr,#0x40000000 ; it ls ; cmpls len,#0x10000000 ; bls ok ; else return 0 (then 0xd12ea4+ : region-overlap checks on [ptr, ptr+len)) It bounds `len <= 0x10000000` (256 MB) and checks the source range -- it does **not** check `len` against any destination buffer size. So a downstream per-channel handler that copies a validated `len` into a fixed-size ASD buffer is still an overflow, and the first-level validation would not catch it. ## Audit pass 1 verdict and where pass 2 goes Negative for the first dispatch level (validated/bounded), but the bug class is intact one level down. Pass 2 targets: * **The per-channel function pointers** `[asd_table + 0x1828/0x183c/0x1c84]`. Resolve the table base (the `ldr r0,[pc,#...]` literal in each worker) and the installed handlers, then look for one that copies the validated `len` into a fixed buffer. These are the real workers; the dispatcher just validates and forwards. * **The unaudited groups** `0x100/0x200/0x300/0x800/0x402/0xFFFF000x`. The `0x800` group (`0xd015a6`) forwards `ldrd r0,r1,[msg+4]` straight to `0xd0d0bc/0xd0d134/0xd0d26c/...` with no visible gate in the dispatcher -- read those for an unvalidated (ptr,len). * **`0xd12e90`'s region checks** (`0xd12ea4+`): confirm whether a TA-window pointer (`0x400000- 0x800000`) with a large `len` passes, which would let a downstream fixed-buffer copy overflow from attacker-readable source. Net: the escalation route (GCP ROP -> ASD service 2 -> type-2 code exec -> fuses/MMIO) is structurally open and the transport+gate are favourable; audit pass 1 cleared the first dispatch level; the bug, if present, is in the per-channel handlers or the unaudited groups. ## Audit pass 2: the 0x800/forwarded group and tail workers are uniformly validated -- negative Read the forwarded workers `0xd0d134/0xd0d26c/0xd0d28c/0xd0d114` and their tail targets `0xd11238/0xd112e4/0xd11348/0xd111c8`, plus `0xd0d0bc/0xd0d170`. Uniform shape, all bounded: * `str #0,[arg2]` zero-inits the caller's 4-byte output pointer; * arg2 validated as a **4-byte** pointer via `0xd12e90` (len fixed at 4, so no overflow of the caller buffer, and nothing copied *into* ASD); * capability gate `0xd116ac`; * channel selector arg1 bounded `<= 0x3f`, index `table + sel*0x60`, state check `[entry+4]==1`; * result is a **single dword written to the validated 4-byte user pointer**, or dispatch through an ASD-internal function pointer `[asd_table + 0x1888/...]`. These are get/set-one-dword-on-a-bounded-channel operations. No message-controlled length, no copy into an ASD buffer, no attacker-controlled index. Negative. ## Pass 1+2 conclusion: ASD's service-2 surface is uniformly validated across what was audited ~15 handlers across the `0x180` and `0x800`/forwarded groups, plus their tail/channel workers, all share the same discipline: capability gate, bounded channel index (`<= 0x3e/0x3f`, stride `0x60`), `0xd12e90` pointer validation, and either fixed-length copies or dispatch through ASD-internal function-pointer tables. This matches the project's documented result that ASD's 37 **host**-reachable opcodes are all bounded (`ASD-REACHABLE-AUDIT.md`); the **service-2 (TA-reachable)** handlers audited here are the same style and equally defended. No memory-safety defect was found. **This is a manual pass, not an exhaustive validated-oracle sweep**, so it does not *close* the surface to the project's standard. What remains genuinely unaudited: * the `0x200`/`0x300` groups (GCP uses `0x201/0x204/0x305`; likely same discipline but unread here); * the exact region semantics of `0xd12e90` at `0xd12ea4+` -- whether a TA-window pointer (`0x400000-0x800000`) with a large `len` passes, which is the only way the "copy len into a fixed ASD buffer" class stays alive, and no audited handler actually performs such a copy; * the per-channel function-pointer handlers (`[asd_table + 0x1828/183c/1888/1c84]`) in full -- they are ASD-internal and not attacker-controlled without a prior write, but a bug inside one reached with a validated pointer could still exist. ## Net for the escalation The route GCP-ROP -> ASD service 2 -> type-2 code exec -> fuses is **structurally open and proven up to the ASD message boundary** (transport works, gate is favourable). But ASD itself is well defended: two audit passes over the most promising groups found uniform validation and no bug. An escalation through ASD would need either an exhaustive validated sweep of all groups + channel handlers, or a different ASD defect class (e.g. a logic/state bug rather than memory safety). The honest status is: capability demonstrated to the ASD door; no hole found in the door yet.