aboutsummaryrefslogtreecommitdiffstats
path: root/dmcub/README.md
blob: 6d812b18954765779b48f00846031bb4bb2699e8 (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
# dmcub/ — the DCN display microcontroller (DMCUB / DMUB)

New target, opened 2026-09. Read in this order:

1. `AUDIT-DMCUB-03-COMMANDS.md` — **the per-command audit of all 24 implemented
   `DMUB_CMD__*` types**, three candidate findings, and the coverage limits.
1. `AUDIT-DMCUB-07-DPAUX.md` — the **newer images** (3.5/3.6/4.0.1): a taint-root
   bug that had made the whole 4.0.1 run a false negative, the retraction of the
   "`DP_AUX_ACCESS` is a stub everywhere" claim, and **FINDING-11**.
2. `AUDIT-DMCUB-02.md` — dispatcher/register-map infrastructure, the address base,
   and the first finding.
3. `AUDIT-DMCUB-01.md` — provenance, container format, ISA determination
   (partially superseded; carries a banner).

Short version: the DMCUB is a **Tensilica Xtensa** (windowed ABI) microcontroller
inside AMD's DCN display IP, PSP-loaded, body mapped at **0x60000000**
(`VA = 0x60000000 + file_off - 0x200`), driven by the host over a 64-byte-frame
inbox ring plus a GPINT doorbell. For Navi 31 the image is
`fw/dcn_3_2_0_dmcub.bin`, 1036 functions, 24 implemented command types.

Three candidate findings so far, all **static only**, all host→DMCUB (the inbox is
driven by amdgpu, so none is a privilege escalation *for* the host — see the threat
model note in `AUDIT-DMCUB-02.md`):

| # | command | primitive |
| --- | --- | --- |
| 1 **[HW]** | `ABM_INIT_CONFIG` (66) | unchecked 16-bit `bytes` → **confirmed on hardware**: ~32 KB of attacker-supplied data written past a 712-byte slot (`bytes=0x8000`, 32492 bytes changed, processed cleanly); `bytes` ≥ 0x9c00 also wedges the command loop |
| 8 **[HW]** | *(firmware loading)* | **DMUB rollback works** — 7.0.38 / 7.0.42 load and run on a card shipping 7.0.51, and a DCN **3.5** image is accepted and starts executing on DCN 3.2 silicon. Signature enforced, version/ASIC binding absent. Dates findings 1/2/3 to ≥ 7.0.38 (2024-02) |
| 6 **[HW]** | `ABM_INIT_CONFIG` (66) | one command with `bytes` ≥ 0x9c00 **wedges the inbox command loop** (display DoS); `SCRATCH0`/timer still look healthy, so liveness must be judged by ring consumption |
| 2 **[HW]** | `GET_VISUAL_CONFIRM_COLOR` (8) | unbounded 16-bit `panel_inst` → 8-byte OOB read returned to the host. **Confirmed on hardware**: indices 2..227 return memory past the 2-entry array. The DoS leg is retracted (out-of-window reads return zeros) |
| 3 **[HW partial]** | `HW_LOCK` (69) | unbounded 8-bit `dig_inst` → register pointer fetched past a 7-entry table, then read-modify-written. **Hardware: reachable and `dig_inst` provably selects the register (1/2/3 → OTG1/2/3), but no OOB write achieved** — the table lives in PSP-owned CW0, so the pointer *value* is never attacker-controlled. Latent, not a primitive |
| 4 **[HW not demonstrated]** | `REPLAY` (83), `CACP` (87), `VBIOS` (128) | handlers run and write state, but the offsets are **index-independent** for idx 0..255; Replay is additionally caller-bounded (`bgeui idx, 2`). Same class: an unchecked `panel_inst`/instance byte scaled by 0x130 / 0x30c / 0xd4 into per-instance state or into the register-address table (Replay's is a plain `s8i` write; AMD's own ABI says that field "is only 0 or 1") |
| 11 **[ST+EMU]** | `DP_AUX_ACCESS` (70), in `yellow_carp`, `dcn_3_1_4`, `dcn_3_5`, `dcn_3_6` | unchecked 8-bit `instance` on the DPIA path → **host-controlled 1-byte write at `base + <off> + instance`, stride 1, up to 252 bytes past a 4-entry array**, repeatable. The sibling function on the same path applies the intended `instance < 4` bound (3×) one call *too late*. Reaches a multiply-referenced adjacent structure at `instance` 8–12. Stays inside the mapped data region (adjacent-global corruption, not a wild write). Stubbed in 3.2.x/4.0.1, which is why a 3.2.0-anchored audit missed it. See `AUDIT-DMCUB-07-DPAUX.md` |

Newer images (`dcn_3_5`, `dcn_4_0_1`) implement 31 command types including
`DMUB_CMD__PSP`, `LSDMA` (host-supplied DMA src/dst/count) and `FUSED_IO`; those are
static-only leads because the 4.0.1 image is refused by this card's PSP and the 3.5
image will not finish booting on DCN 3.2. `DP_AUX_ACCESS` (70) is a **stub in**
3.2.0 **and** 4.0.1 — but **not** in 3.5/3.6, where it is fully implemented and
carries FINDING-11; the earlier generalisation that "there is no AUX sink path via
that command in any image we hold" was **retracted** in
`AUDIT-DMCUB-07-DPAUX.md`. Only the two stub images had been checked.

Also recorded **[HW]**: the host **cannot** program `DMCUB_REGION3_CW0/CW1` or
`DMCUB_SEC_CNTL` (writes dropped, reads 0), so `backdoor_load` is blocked on Navi
31 and DMCUB code execution requires a PSP-signed image — a security-positive
result, and the reason bring-up goes through `GFX_FW_TYPE_DMUB`.

Also recorded: types 1/3/4 are the legacy `REG_SEQ_*` / `REG_REG_WAIT` commands and
are an *intentional* arbitrary register read/write/poll interface. They are
**documented ABI**, not "unpublished" as this tree said until corrected — present in
`enum dmub_cmd_type` from v5.6 through v6.18-rc1 and deleted from the header only by
upstream commit `9a591ae691b5` (2026-06-05, "command IDs 1-4 have been deprecated").
`fetch_ref.sh` pulls master, which is why they looked undocumented. **The firmware
still dispatches them in every image in `fw/`, including DCN 4.0.1** — the
deprecation is header-only. Name IDs from the pinned `ref/dmub_cmd_v6.17.h`;
`DP_AUX_ACCESS` (70) is a **stub** in this build (3.2.0 only — see FINDING-11 for
3.5/3.6). Bounded and closed: EDID/CEA
reassembly and its VSDB parse, all 23 `jx` dispatches, 27/28 `callx8` sites (the
one indexed site is double-guarded), and 55 of 57 memcpy sites.

## Tooling

Xtensa needs **capstone 6**; the system capstone (5.0.9) has no `CS_ARCH_XTENSA`:

```sh
python3 -m venv /home/user/.venvs/cs6 && /home/user/.venvs/cs6/bin/pip install --pre capstone
```

`dmcub_cfg.py` and `dmcub_regmap.py` must run under that interpreter; the other
scripts work with the system python. IDA 9.3/9.4 also ship `procs/xtensa.so`
(this corrects the note in `psp_out/FIRMWARE-INVENTORY.md`).

```sh
./fetch_ref.sh                                       # kernel reference sources -> ref/
python3 dmcub_parse.py fw/*.bin                      # container + dmub_fw_meta_info
python3 dmcub_isa_evidence.py fw/dcn_3_2_0_dmcub.bin 0x1c100 0x44000   # ISA + controls

V=/home/user/.venvs/cs6/bin/python
$V dmcub_dis.py  fw/dcn_3_2_0_dmcub.bin hot 12        # hottest call targets
$V dmcub_dis.py  fw/dcn_3_2_0_dmcub.bin dis 0x6001c780   # the inbox1 dispatcher
$V dmcub_regs.py fw/dcn_3_2_0_dmcub.bin ref/dcn_3_2_0_offset.h access --grep INBOX
$V dmcub_regs.py fw/dcn_3_2_0_dmcub.bin ref/dcn_3_2_0_offset.h vote   # aperture evidence
$V dmcub_jxaudit.py   fw/dcn_3_2_0_dmcub.bin          # jump-table bounds audit
$V dmcub_copyaudit.py fw/dcn_3_2_0_dmcub.bin          # memcpy length audit
$V dmcub_dpaux_audit.py fw/*.bin fw/old/*.bin         # FINDING-11 cross-version sweep
$V dmcub_emu_dpaux.py   --selftest                    # FINDING-11 in emulation (7 controls x2 images)
```

`dmcub_cmdaudit.py` auto-detects which register holds the command-frame pointer
(`a3` on 3.2.x/3.5/3.6, **`a4`** on 4.0.1/renoir) and prints a `WARNING: ... Treat
every "0 sinks" result in this run as UNVERIFIED` line if taint never entered a
case's handlers. Do not trust a run that prints it — that warning exists because a
hardcoded `a3` silently produced a clean audit of 30 unexamined command types.

## Hardware PoC (Option A)

`hw/` brought the DMCUB up on the rig and **reproduced FINDING-2 on silicon**
(2026-09). The tinygrad-AM stack initialises no display IP, so the core is loaded
via the PSP (`GFX_FW_TYPE_DMUB`, stock signed image) and then driven over inbox1.
Read `hw/README.md`; transcript in `hw/EVIDENCE-type8-poc.txt`.

## Contents

* `fw/` — 10 `linux-firmware` images, Renoir .. DCN 4.0.1 (hashed in
  `data/MANIFEST.sha256`).
* `ref/` — **untracked** upstream kernel sources (register offsets, `dmub_cmd.h`
  ABI, loader); re-fetch with `fetch_ref.sh`.
* `AUDIT-DMCUB-01.md` — the audit.
* `dmcub_*.py` — analysis tools, described at the bottom of the audit.
* `hw/` — hardware bring-up + PoC scripts (probe, bring-up, type-8 PoC), dry-run
  by default, plus offline selftests.