aboutsummaryrefslogtreecommitdiffstats
path: root/fw/FINDING-PSP-RING-BASE-ALIGNMENT-UNCHECKED.md
blob: 6a0f4c8593f2e851422c5e85674fb23a01e3ebbb (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
# FINDING: the PSP ring base is accepted misaligned, then wedges on first submit

**Evidence class: [HW]** — Navi 31 / RX 7900 XTX, sOS/SYS_DRV 0.49.0.54.
**Status: missing check CONFIRMED. Eighth DoS trigger. No memory corruption.**

## The ring-creation surface

The ring is created by writing its base (`C2PMSG_69/70`), its size (`_71`) and a create
command to `_64`, whose low 16 bits are the PSP's own status — so the firmware reports
acceptance directly and nothing has to be inferred. `hw/ring_create_fuzz.py`, one case per
fresh boot:

| case | status | outcome |
|---|---|---|
| control: correct base and size | `0x0` | accepted, commands execute |
| size `0` | **`0x4`** | rejected |
| size `1` | **`0x4`** | rejected |
| size `0xFFFFFFFF` | **`0x4`** | rejected |
| size `0x80000000` | **`0x5`** | rejected |
| **base inside the TMR** | **`0xa`** | **rejected — explicitly range-checked** |
| base `0` | — | rejected (ready bit never set) |
| **base misaligned (+1)** | **`0x0`** | **ACCEPTED**, then wedges |

This validation layer is otherwise **good**: four distinct rejection codes, and the trusted
region is explicitly excluded from the ring base.

## The defect

`hw/ring_unalign.py` runs the aligned control **first in the same boot** — it succeeds, the
heartbeat advances — and only then creates a ring at `base + 1`:

```
  aligned base (control)   create status=0x0 ACCEPTED   submit resp=0x0      hb_advancing=True
  base+0x1                 create status=0x0 ACCEPTED   submit resp=TIMEOUT  hb_advancing=False
```

The misaligned base is **accepted at creation** and the PSP then **dies on the first submit**.
Because the control passed moments earlier in the same boot, this is not an inherited dead
PSP — the failure mode that produced phantom hits twice in this audit.

## Why it is a real inconsistency, not a policy choice

Alignment *is* enforced everywhere else in this interface:

* `cmd_buf` misaligned ⇒ rejected `0x6` (even at `+0x800`),
* TA image misaligned ⇒ rejected `0x34`,
* ring base inside the trusted region ⇒ rejected `0xa`,
* ring size bogus ⇒ rejected `0x4` / `0x5`.

Only the ring base's **alignment** is unchecked, and the consequence is deferred to first use.
This is the same shape as `LOAD_TA` with `cmd_buf_len = 0` (accepted at load, hangs on first
invoke): a parameter validated incompletely, with the failure surfacing later as a hang.

## Not memory corruption

The PSP hangs; it does not write anywhere it should not. Recovery requires
`work/atx_power.py cycle 25`; GPU/AM reset is insufficient.

## Suggested fix

Check the ring base's alignment at creation and reject it with a status, as the size and the
trusted-region overlap already are.