aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 8ff051156591c2134e62ec347d498193fe6737a0 (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
# amd-psp-re

Reverse engineering, emulation and vulnerability research on the **AMD Platform
Security Processor (PSP)** as shipped on Navi 31 (Radeon RX 7900 XTX), plus the
neighbouring on-die co-processors (SMU, MEC).

Split out of a `tinygrad` working tree; `tinygrad` remains a *dependency* for
everything that talks to real hardware.

---

## Start here

`psp_out/INDEX.md` is the map: which documents are authoritative, which are
retracted, which questions are settled negatives that should not be re-run, and
the current priority list. Several filenames outlived their conclusions, so
**check a document's row in the index before citing it**.

The four governing documents are `CONSTRAINTS.md` (standing rules, read before
touching hardware), `VULN-AUDIT-WHAT-WE-ACTUALLY-HAVE.md` (the vulnerability
register), `ATTACK-SURFACE.md` (the surface map) and `INDEX.md` itself.

## What is actually in here

Two things, and it is worth keeping them apart:

**1. Static reversing output** (`psp_out/`) — all six PSP firmware modules fully
named and structured, a cross-version diffing pipeline over 34+ firmware builds,
per-handler annotated analysis, and ~125 findings documents.

| module | functions named |
| --- | --- |
| `asd` | 1562 |
| `sys_drv` | 1141 |
| `sos` | 527 |
| `ras` | 119 |
| `hdcp` | 109 |
| `dtm` | 14 |
| SMU (Xtensa) | 1033 |

**2. A working PSP emulator** (`psp_emu/`) — boots the real signed firmware
under Unicorn, from the ARM reset vector, with a CCP (crypto co-processor)
device model faithful enough that the firmware's own drivers run against it.

It currently runs the **whole system in a single boot** (`harness/full_system.py`):
sOS → SYS_DRV → HDCP, DTM and RAS, plus the ASD driver arriving over the host
ring, with every module's execution attributed by address space *and* live
physical page rather than by a shared virtual address. A
simulated host can drive the real mailbox/ring command path end to end:

```
host writes mailbox header + ring frame
  -> IRQ 80 (doorbell)
  -> bl_legacy_mailbox_loop -> tee_ring_drain -> tee_cmd_dispatch
  -> tee_load_ta -> bl_load_ta_image   (full RSA-PSS/SHA-256 on the CCP model)
  -> session slot -> tee_ta_command -> TA_InvokeCommandEntryPoint
```

## Honest status of the security work

No host-reachable memory-corruption primitive has been found, and the reason
appears to be **structural** rather than "we did not look hard enough": the
host-invokable TA surface is confined to three syscalls, and an exhaustive audit
of all 37 host-reachable ASD opcodes plus a four-class write-shape scan over a
988-function closure came back bounded.

What *was* found:

* **One proven heap overflow** — `gp_operation_copy_state` (GP opcode 0x37),
  byte-exact reproduction in-emulator. **TA→kernel only, with no emitter**, so
  it is not host-reachable.
* **One real firmware bug found by emulation** — `ccp_hmac_sha2` computes
  HMAC-SHA512 incorrectly (the opad IV memcpy clobbers the last 16 bytes of the
  inner digest). Dead code in-tree: both callers pass alg=4.
* **Four latent unbounded-index / uncapped-counter defects**, all currently
  inert.
* An HDCP subcommand-12 overflow proven to be a **DoS only**.

Several earlier conclusions were **retracted** after better evidence; retractions
are recorded in place rather than deleted. If a document contradicts a
hardware-confirmed note, the hardware note wins.

## Layout

```
psp_emu/                emulator
  pspemu/               core: CPU/bus/MMU (armmmu.py), CCP model (ccp.py), SVCs
  harness/              runnable experiments (boot, fuzzers, PoCs, self-tests)
  tests/                test_smoke.py
  symbols/              symbol maps consumed by the emulator
psp_out/                static analysis
  *.md                  findings, audits, vulnerability write-ups
  *.py                  analysis tools + hardware probes
  handlers/             per-command annotated analysis (hand-written)
  phase3/ vdiff/ sigs/  cross-version symbol maps and diff output
  handover/             importable symbol sets + import_symbols.py
data/MANIFEST.sha256    SHA-256 of every firmware blob results were derived from
```

## Data policy — what is and is not in the tree

**Tracked:** the firmware images themselves (`*.bin`, `*.elf`, 301 files,
~33 MB). The PSP and SMU module images come from `linux-firmware` and publicly
mirrored VBIOS images, so they are already public. **Nothing here was
decrypted** — the encrypted on-flash stages (`BOOT_LOADER#1`, `TRUSTED_OS`,
entropy ~8.0) remain encrypted and unanalysed; sOS is the earliest stage we
possess. The whole-flash dumps carry board SKU strings only
(`XFX D702_XTX`, `113-31XFSHBS1`) — no serials, MACs or unique identifiers.

`data/MANIFEST.sha256` pins the SHA-256 of all 301 images, so a result can be
tied to the exact bytes it came from even after a re-dump.

**Not tracked:**

* `*.i64` — IDA databases (228 MB), regenerable from the blobs plus
  `psp_out/handover/import_symbols.py`, and they churn on every incidental
  click. The reversing work itself is preserved as symbol maps in
  `psp_out/phase3/`, `vdiff/`, `sigs/` and `handover/symbols/`, which is the
  part with actual value.
* `psp_out/decompiled/` — Hex-Rays output mechanically derived from AMD code
  (26 MB).
* AMD-internal register maps (`nv22_a0_memmap.txt`, `map2.txt`) — vendor
  documents, a different provenance from public firmware.

## Setup

```sh
cd psp_emu
python3 -m venv .venv && ./.venv/bin/pip install -r requirements.txt
./.venv/bin/python tests/test_smoke.py          # 5/5
./.venv/bin/python harness/ccp_selftest.py      # 11/11
./.venv/bin/python harness/verify_real_image.py # 5/5 + 5/5 negatives
```

Hardware scripts in `psp_out/` additionally need `tinygrad` on `PYTHONPATH` and
a real GPU; they are the only things here that touch a device.

## Working rules this project runs on

Earned the hard way, mostly by being wrong first:

* **Validate every detector against ground truth with positive *and* negative
  controls.** A negative result from an unvalidated oracle is not a result. The
  TA fuzzer refuses to report "no crashes" until its positive control fires.
* **A negative is only meaningful if the harness demonstrably reaches the
  code** — measure coverage, not the pass counter.
* **Verify the emitter, and the values it supplies, before valuing the
  handler.** Static reachability is not reachability.
* **Model SVCs faithfully rather than patching validators**, and prefer running
  the firmware's own kernel over stubbing it. A stub returning 0 is not neutral;
  it is a silent lie that surfaces much later.
* **Self-consistent tests can be wrong together** — the CCP RSA engine passed
  6/6 only because the harness shared its mistake.
* **Deterministic execution before bug-hunting.** A flaky harness produces
  confident wrong negatives.
* **Record retractions in place** and propagate them to referencing documents.

## Known tech debt

* ~86 hardware scripts in `psp_out/` hardcode an absolute repo path (rewritten
  on the move from the tinygrad tree). They should resolve paths from a shared
  module or `$PSP_OUT`; the emulator harnesses already resolve relatively.
* 16 scripts hardcode the tinygrad checkout path for imports; should honour
  `$TINYGRAD_ROOT`.
* `psp_out/` mixes offline analysis tools with hardware probes; these deserve
  separate directories.