aboutsummaryrefslogtreecommitdiffstats
path: root/fw/AUDIT-GCP-ROP-TRIGGERABLE-SVCS.md
blob: 14acfc51f6f5286d291ef7bb76ad21ef38537a83 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# What SVCs a ROP'd GCP TA can actually trigger -- the accurate, measured picture

Correcting the earlier "16 SVCs reachable" (that was the GATE's *allowance*, not our *capability*).
Two independent limits apply, and the real triggerable set is their intersection.

## Limit 1: we can only issue SVC immediates that exist as even-aligned `svc #imm` instructions

The SVC number is encoded in the instruction (`0xDF imm8`), not a register, and GCP's `.bss`/heap is
**not executable** (hijack report section 5), so we cannot forge new `svc` instructions -- only reuse
the ones GCP's code already contains. Comprehensive scan of GCP `.77` `[0x400000,0x407000)`,
even-aligned real instructions only:

    svc 0xf2  -- 26 sites (plenty of usable gadgets)
    svc 0xf3  -- 1 site
    svc 0xf4  -- 1 site
    svc 0xf5  -- 1 site

Nothing else is even-aligned. `svc 0xf0` appears only at odd offsets (mid-instruction `0xDF` bytes,
not executable). The tlApi page (`0xf00000`) adds only `svc 0x66` at `0xf00780`. **So `0xE0-0xE9`
(the privileged aliases the gate would allow) are NOT issuable -- no gadget exists for them.**

## Limit 2: the dispatch gate (type-5) -- moot here, since 0xf2-0xf5 are all >= 0xf0

`0xf0+` take no type check (`AUDIT-TA-KERNEL-BOUNDARY`), so all four pass. `svc 0x66` (tlApi) is
`<= 0xDF` and type-5-gated, so it is rejected -- not usable.

## The triggerable set and its audit (sos2.elf `sub_6450`, the 0xf0-0xf5 dispatcher)

| SVC | handler | what it does | verdict |
|---|---|---|---|
| **0xf2** | `sos_proc_switch` (`0x22B4`) | RPC to a service with r0=service id, r1=msg | **the entire escalation surface** |
| 0xf3 | returns `dword_64BC` | a boot constant | inert |
| 0xf4 | `*(pcb_base + 96*PCB[75] + 28)` | reads the **caller's OWN** PCB field +0x1C | inert (own data) |
| 0xf5 | `*(pcb_base + 96*PCB[75] + 32)` | reads the caller's OWN PCB field +0x20 | inert (own data) |

(0xf0 wait and 0xf1 setexit are benign anyway, and not issuable.) **So of everything a ROP'd GCP TA
can trigger, only `svc 0xf2` is useful. The whole TA->kernel attack surface is `svc 0xf2` -> a
service.** `0xf3/0xf4/0xf5` read a constant or the caller's own PCB -- no escalation value.

## svc 0xf2 reach -- the real audit target

`sos_proc_switch` with the type-5 caller (GCP) permits:

* **service 1 = SYS_DRV** (permission-exempt), and **service 2 = ASD** (TA has cap bit 0x80). Service
  3 needs caller type 1 -- unreachable.
* Both require SP and msg in `[0x400000,0x800000)` -- satisfied by the stack-migration chain
  (Step 5b/6, proven on silicon).

### SYS_DRV (service 1) opcode reachability is gated, and the gate is BOOT-STATE, not static

`sub_E087E4` -> (`opcode>=0x1000`) `sub_E105A8` -> `sub_E05A24` opcode-range ACL:

    find the {lo,hi,mask} range for the opcode;  issue SVC 0x76;  reject (0xffff0001) if the mask's
    caps are absent;  opcodes OUTSIDE every range are allowed unconditionally.

SVC 0x76 is `(reqmask & ~off_6428[2*PCB[75]]) == 0`, and **`off_6428` (the capability table) is
BSS filled at boot** -- so which ranges pass CANNOT be read from the firmware image. It must be
measured on silicon. Measured so far: range `0x3100-0x31FF` (mask `0xffff0`, contains the overflow
handlers `0x3102/0x3103`) is **REJECTED** (`0xffff0001`). The ACL ranges are:

    0x1100-0x11FF m=0x200   0x2000-0x20FF m=0x40    0x3000-0x30FF m=0x1000  0x3100-0x31FF m=0xffff0 (REJECTED)
    0x4000-0x400F m=0x2000  0x6000-0x60FF m=0x4000  0x7000-0x70FF m=0x8000  0x8000-0x80FF m=0x10000
    0x103D m=0x400          0x103F m=0x800
    everything else (incl. <0x1100, 0x1200-0x1FFF where ASD's own 0x1021/0x103B.. live): FREELY ALLOWED

## The audit plan (accurate = measured)

1. **Map reachable SYS_DRV opcodes on silicon.** Reuse the stack-migration chain, vary the opcode,
   read the exfil'd status: `0xffff0001` == ACL-gated; a handler status == reachable. One shot per
   ACL range (10 ranges) + a couple of free opcodes gives the full reachability map.
2. **Audit the reachable handlers** (free opcodes + any allowed range) for memory safety -- the
   overflow handlers `0x3102/0x3103` are gated out, so the hunt is now among the *allowed* opcodes
   (the large free space `<0x1100`/`0x1200-0x1FFF`, plus whatever ranges pass).
3. **ASD (service 2)** is the other reachable service; its dispatcher surface (passes 1-2 of
   `LEAD-ASD-SERVICE2-ATTACK-SURFACE.md`) was uniformly validated at the first two levels.

Bottom line: the accurate directly-triggerable SVC set is `{0xf2,0xf3,0xf4,0xf5}`, of which only
`0xf2` matters; and `0xf2`'s SYS_DRV reach is gated by a boot-filled capability table that only
hardware can resolve. The overflow opcodes are gated out; the remaining surface is the ACL-allowed
opcodes, to be mapped on silicon and audited.

## MEASURED on silicon: the SYS_DRV svc-0xf2 opcode reachability map

`hw/gcp_rop_sweep.py` + `hw/drv_sweep.sh` -- the stack-migration chain, one opcode per ACL range,
fresh ATX per shot, reading the exfil'd SYS_DRV status (`0xffff0001` == ACL-rejected):

    OPCODE 0x1021 (free, no range)     status 0x0          REACHED  (handler ran)
    OPCODE 0x3103 (range 0x3100)       status 0xffff0001   ACL-REJECTED
    OPCODE 0x3005 (range 0x3000)       status 0xffff0001   ACL-REJECTED
    OPCODE 0x2005 (range 0x2000)       status 0xffff0001   ACL-REJECTED
    OPCODE 0x1105 (range 0x1100)       status 0xffff0001   ACL-REJECTED
    OPCODE 0x4001 (range 0x4000)       status 0xffff0001   ACL-REJECTED

**Every ACL range tested is rejected; only free (out-of-range) opcodes pass.** So `off_6428` grants
the caller NONE of the range masks (`0x200/0x40/0x1000/0xffff0/0x2000/...`). This settles the
confused-deputy question accurately: `SVC 0x76` checks the **real caller's (GCP, type 5) minimal
caps** -- the ACL is NOT fooled by the nested RPC. The confused deputy only defeats
`sos_proc_switch`'s *permission* bit (dest-1 exempt); the opcode-range ACL stands.

### Accurate reachable SYS_DRV surface via GCP ROP -> svc 0xf2 service 1

* **Crypto opcodes `< 0x1000`** -> `sub_E1DC30` (crypto dispatcher). No opcode-range ACL on this
  path. Reachable. (These are the MAC/hash/init/setkey ops HDCP uses.)
* **High opcodes `>= 0x1000` OUTSIDE every ACL range** -> `sub_E105A8`, allowed unconditionally.
  Reachable. This includes `0x1000-0x10FF` (except `0x103D/0x103F`), `0x1200-0x1FFF`, `0x5xxx`,
  `0x9xxx+`, and is exactly where ASD's own emitted opcodes (`0x1021/0x103B/0x103C/0x1043/...`) live.
* **ACL-range high opcodes** (`0x1100-0x11FF, 0x2000-0x20FF, 0x3000-0x31FF, 0x4000-0x400F,
  0x6000-0x60FF, 0x7000-0x70FF, 0x8000-0x80FF, 0x103D, 0x103F`) -> **UNREACHABLE** (caps absent).

### Consequence for the escalation

The stored-length overflow (#9, `sub_E0F364`/`sub_E0B090`, opcodes `0x3102/0x3103`) lives in the
`0x3100` range -> **confirmed UNREACHABLE on silicon** from this path. That specific escalation is
closed. The reachable SYS_DRV surface is now precisely: the crypto dispatcher (`<0x1000`) and the
free high-opcode handlers (`>=0x1000`, out of range). Those are the handlers to audit for a
memory-safety defect -- and much of the free high-opcode space overlaps what
`fuzz_sysdrv` already exercised via the ASD-emitted opcodes. The audit continues there, not in the
gated ranges.

## MEASURED (round 2): the 0x6000/0x7000/0x8000 ranges are ALSO rejected from GCP

Prompted by `FINDING-SYSDRV-ACL-RANGE-TABLE.md`, which documents three ACL families the first sweep
missed -- and they hold the most interesting primitives:

    0x6001 = otp_block_read        (OTP / FUSE read)        range 0x6000-0x60FF, mask 0x4000
    0x6002 = tlv_buffer_copy       (SMN MMIO-write, 64 writes/call, attacker values)  mask 0x4000
    0x7001/0x7002 = ta_reg_access  (register write/read, allowlist-gated)  range 0x7000, mask 0x8000
    0x8000-0x80FF                  mask 0x10000

Swept them from GCP via the same chain:

    OPCODE 0x1021 (free)  status 0x0         REACHED  (control)
    OPCODE 0x6001 (OTP)   status 0xffff0001  ACL-REJECTED
    OPCODE 0x6002 (MMIO)  status 0xffff0001  ACL-REJECTED
    OPCODE 0x7001 (reg w) status 0xffff0001  ACL-REJECTED
    OPCODE 0x7002 (reg r) status 0xffff0001  ACL-REJECTED
    OPCODE 0x8001         status 0xffff0001  ACL-REJECTED

**All of them rejected.** Combined with round 1, every one of the 8 distinct ACL range masks
(`0x200, 0x40, 0x1000, 0xffff0, 0x2000, 0x4000, 0x8000, 0x10000`) is denied to the GCP caller. GCP's
capability entry in `off_6428` grants **none** of the gated ranges.

### Definitive reachability from a ROP'd GCP TA

Reachable via `svc 0xf2` service 1: **only** the crypto dispatcher (`opcode < 0x1000`, `sub_E1DC30`)
and the **free** high opcodes (`>= 0x1000`, outside all 10 ACL ranges, `sub_E105A8`). Every gated
range is unreachable. So from GCP the following SYS_DRV primitives are all **out of reach**:

* the stored-length overflow `0x3102/0x3103` (range 0x3100);
* **OTP / fuse read `0x6001`** (range 0x6000) -- this is the definitive answer to "can we read fuses":
  **not from GCP**;
* the SMN MMIO-write `0x6002` (range 0x6000);
* TA register read/write `0x7001/0x7002` (range 0x7000).

### Strategic implication

The gated ranges are reachable only from a TA whose caps hold the matching bit: the ACL-table doc
notes **POWERPLAY emits `0x6001/0x6002/0x6003`** and **RAS emits `0x70xx`**, so those TAs hold the
`0x4000` / `0x8000` caps that GCP lacks. Reaching the OTP read or MMIO-write (the high-value
primitives) therefore requires compromising **POWERPLAY** (for `0x60xx`) or **RAS** (for `0x70xx`)
-- a different TA with the right capability entry -- not GCP. GCP ROP is a dead end for the gated
SYS_DRV surface; its only SYS_DRV reach is the crypto dispatcher and the free high opcodes.

## How many "free" opcodes can we actually call? ~164 (enumerated from the .54 dispatchers)

Enumerated the handled opcodes in both SYS_DRV dispatchers (`sys_drv2.elf` = card's `.54`) and
classified each against the 10 ACL ranges:

    CRYPTO  sub_E1DC30 (<0x1000, no ACL):   0x1 - 0x9                                     9 free
    HIGH    sub_E105A8 (ACL-gated):         0x100a-0x10a4 (minus gated 0x103d/0x103f)
                                            + 0x4013/0x4014/0x4015                      155 free
                                            ------------------------------------------------
                                                                        TOTAL FREE  ~= 164

    HIGH gated (unreachable from GCP): 122 opcodes -- the 0x1100/0x2000/0x3000/0x3100/0x4000-400f/
    0x6000/0x7000/0x8000/0x103d/0x103f ranges, i.e. every high-value primitive
    (overflow 0x3102/0x3103, OTP read 0x6001, MMIO-write 0x6002, reg access 0x70xx).

So from a ROP'd GCP TA the reachable SYS_DRV service-1 surface is **~164 opcodes**: the 9 crypto ops
and the ~155-opcode `0x1000-0x10A4`(+`0x4013-5`) band. That band is exactly the "0x1000-0x10A6"
family the project's original model named, and it is the surface `fuzz_sysdrv` already exercised via
ASD's emitted opcodes (`0x1021/0x103B/0x103C/0x1043/0x1044/0x1057/0x1079/0x1087/0x1095/0x109A/0x109B`)
and found bounded. The 9 crypto ops (`0x1-0x9`) are the MAC/hash/init/free/setkey family (HDCP's
usage), also audited.

### Net

Callable free SYS_DRV surface from GCP: ~164 opcodes, almost entirely already-audited (the
0x1000-0x10A4 high band + 0x1-0x9 crypto). The 122 gated opcodes -- which hold every unaudited
high-value primitive -- are unreachable from GCP's caps. A fresh memory-safety hunt from GCP would
re-cover `fuzz_sysdrv`'s ground; the unaudited high-value surface requires a different caller
(POWERPLAY for 0x60xx, RAS for 0x70xx).