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
|
# Hardware probes — what each one tests and what it found
All probes run on the Navi 31 test part:
`AM_RESET=1 PYTHONPATH=/root/psp/tinygrad python3 hw/<probe>.py`
Recovery after a wedge: `PYTHONPATH=/root/psp/tinygrad python3 work/atx_power.py cycle 25`.
Most probes carry an in-run **control**; a run whose control fails proves nothing and several
results in the audit log were discarded on exactly that basis.
## Infrastructure
| probe | purpose |
|---|---|
| `ring_wrap.py` | wrapping PSP ring submit. tinygrad's `_ring_submit` never wraps the write pointer, so every session was capped at ~1008 invokes and then wedged. Import `install(psp, adev)`. **Required by the deep fuzzers.** |
## Denial-of-service triggers (all [HW] reproduced, all hangs)
| probe | finding |
|---|---|
| `auc_cmd6_wedge.py` | AUC cmd 6 wedges the PSP; cmds 4/5 as in-run controls |
| `ta_cmdfuzz.py` | the 12-TA command sweep that found AUC cmd 6 and FP cmd 7 |
| `len0_test.py` | `LOAD_TA` with `cmd_buf_len = 0` is accepted, then the first INVOKE hangs |
| `fp_lenthresh.py` | FP cmd 2 completes at `decl=0x4000`, hangs at `0x2000` — parameter-driven |
| `aperture_test.py` | `cmd_buf` = MC 0 hangs, with a VMC fault flood: no aperture validation |
| `auc_wedge_state.py` | register evidence that the class is a **hang, not a fault** (sOS alive, heartbeat frozen) |
| `auc_sid_test.py`, `auc_len_test.py`, `fp_cross.py` | hypotheses tested and **rejected** (session index, underflow, cross-TA state) |
## Boundary checks found CORRECT
| probe | result |
|---|---|
| `cmdbuf_tmr.py`, `tmr_edge.py` | TMR range-checked on both descriptor halves, end-exclusive, no off-by-one |
| `tmr_resetup.py` | `SETUP_TMR` cannot be re-issued — the region is not relocatable |
| `app_range.py` | the TA image source is range-checked too; `app_len = 0` rejected cleanly |
| `align_test.py` | misaligned `cmd_buf`/image rejected; no rounding, no stray writes |
| `oversized_decl.py` | over-declaring `cmd_buf_len` does **not** cause out-of-bounds writes |
| `image_parser_fuzz.py`, `hdr_field_test.py` | pre-auth image parser robust; the apparent signature gap is optional-field semantics |
| `session_uaf.py` | stale, double-freed and forged session handles all rejected |
| `session_churn.py` | 500 load/unload cycles: no slot leak, ids recycled |
| `session_interleave.py` | 8 concurrent sessions, 480 invokes: no cross-session leakage |
## Ring frame (the structure itself, not the command)
| probe | result |
|---|---|
| `fence_write.py` | the PSP writes a host-chosen value to a host-chosen address |
| `fence_tmr2.py <scratch\|tmr\|zero>` | `fence_addr` in the TMR is **accepted** (`cmd_buf` is rejected); `fence_addr=0` **wedges** |
| `fence_land.py` | host reads of the TMR return `0xffffffff`, so read-back cannot settle it |
| `fence_spray.py N STRIDE` | dense functional test: 16384 dwords / 64 B over the first TMR megabyte, TA behaviour unchanged |
| `frame_sweep.py [off]` | all 16 frame dwords, re-running the control after every mutation so a dead PSP cannot fake hits |
| `vmid_test.py <vmid> <ftype>` | any non-zero `vmid` wedges the PSP (7th trigger); `frame_type` ignored |
| `vmid_proof.py [conf]` | configuring MM VM context 1 does not make `vmid=1` work — mechanism only partly characterised |
## Ring creation and control space
| probe | result |
|---|---|
| `ring_create_fuzz.py <case>` | creation is well validated: bogus sizes `0x4`/`0x5`, base inside the trusted region `0xa` |
| `ring_unalign.py` | but a **misaligned base is accepted** then wedges on first submit (8th trigger) |
| `ring_wptr_oob.py` | the ring index is bounded, and out-of-range is ignored rather than fatal |
| `gfx_ctrl_sweep.py <id>` | all 14 control ids inert, including five undocumented |
| `gbr_ih_set.py <case>` | **VACUOUS** — every address including the valid one returns `0x124`, so the path is non-functional here and proves nothing about its validation |
## Bootloader interface and rollback
| probe | result |
|---|---|
| `bl_runtime_probe.py <case>` | the bootloader mailbox is **live at runtime**: answers a bogus component id with status `0xa` while sOS runs |
| `bl_runtime_load.py <comp>` | but **refuses to act** post-boot — valid loads get the same `0xa` as a nonsense id (uniform refusal) |
| `rollback_inspect.py` | extracts SYS_DRV from historical `psp_13_0_0_sos.bin` blobs and version-compares with the running build |
| `rollback_boot.py <sos\|identity>` | substitutes a SYS_DRV during init; `identity` is the positive control |
| `rollback_verify.py <sos\|identity>` | **no anti-rollback**: after a cold cycle, 49.0.37 boots on a 49.0.54 part and services TAs |
## Memory-safety sweeps (all negative)
| probe | coverage |
|---|---|
| `ta_leak_probe.py` | disclosure: 10 TAs, PSP-internal-pointer shapes in returned data |
| `ta_oob_write.py` | writes past the declared window, 9 TAs with valid controls |
| `corpus_oob.py` | the same over **63 distinct corpus TA builds**, commands 0-11 |
| `ta_deepfuzz.py` | randomized multi-field mutation; full window after pass 111 |
| `deep_param_sweep.py` | **exhaustive** single-dword sweep of the entire 16 KB window |
| `invoke_struct_fuzz.py`, `field24.py` | the 784-byte INVOKE ring structure; one validated field, characterised |
| `loadta_sweep.py` | full-resolution sweep of the LOAD_TA structure |
| `ta_seqfuzz.py` | stateful sequences: output carried into the next invoke |
| `ta_multisess_seq.py` | multi-session stateful fuzz with slots recycling underneath |
| `ta_handlefuzz.py` | handle replay at offset +4 only — **superseded** by `handle_offsweep.py` |
| `handle_offsweep.py` | handle replay at **every** dword offset, vs a random-value control (closes pass 94's weak spot) |
| `gfx_undoc.py`, `gfx13_probe.py` | the last two unread GFX commands |
| `asd_load_sweep.py` | all 17 corpus ASD builds through `LOAD_ASD`, with a PSP-health control after each |
## Earlier rounds (GCP lifetime work)
`gcp_fe0d_refcount_probe.py`, `gcp_fe0f_stale_slot_probe.py`,
`gcp_acquire_gate_bypass_poc.py`, `gcp_asd_differential_sweep.py`,
`gcp_asd202_leak_probe.py`, `invoke_newtas_probe.py`, `loadta_windows_sweep.py`.
|