aboutsummaryrefslogtreecommitdiffstats
BranchCommit messageAuthorAge
mainRecord PSP security investigation artifacts and probesuser3 weeks
 
 
AgeCommit messageAuthor
2026-10-09Record PSP security investigation artifacts and probesHEADmainuser
2026-10-08GCP->ASD 0x403: ASD-internal chain PROVEN end-to-end in emulator (real handlers)user
2026-10-08GCP->ASD lead: aliasing resolved for our build (index 90) + HW service-2 reac...user
2026-10-08eval: GCP->ASD op_403 confused-deputy -> SVC 0x7C privileged physical write (...user
2026-10-08hw: no deterministic crypto-output placement - SVC 0xB5 client check (sub_197...user
2026-10-08sim: confirm GCP crypto ROP message is correct; localize 0xFFFF000F to SVC 0x...user
2026-10-08crypto output oracle: ABANDON - confused-deputy output-buffer validation is r...user
2026-10-08crypto output read-back: principle OK (create handle), but RNG 'identical out...user
2026-10-08re-evaluate GCP attack surface (post-retraction): capability-capped SYS_DRV r...user
2026-10-08overview: correct the retracted SYS_DRV-hijack claim -> GCP self-corruption +...user
2026-10-08RETRACTION: the SYS_DRV control-flow hijack was GCP self-corruption of our ow...user
2026-10-08overview: full GCP-invocable syscall/op surface by context + constraintsuser
2026-10-08op 0x3a pointer-relay NOT viable (metadata not code pointers); close crypto-w...user
2026-10-08path-1 attempt via op 0x36 ctx-copy confirms the controlled-value barrieruser
2026-10-08controlled-value analysis: chosen PC gated by confused-deputy marshaling (PC-...user
[...]
 
Clone
https://git.sl0p.foo/amd-psp-re.git
ssh://git@git.sl0p.foo:39327/srv/git/amd-psp-re.git

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

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.