aboutsummaryrefslogtreecommitdiffstats
path: root/dmcub
ModeNameSize
-rw-r--r--AUDIT-DMCUB-01.md14575logstatsplainblame
-rw-r--r--AUDIT-DMCUB-02.md17422logstatsplainblame
-rw-r--r--AUDIT-DMCUB-03-COMMANDS.md38577logstatsplainblame
-rw-r--r--AUDIT-DMCUB-04-LSDMA.md6938logstatsplainblame
-rw-r--r--AUDIT-DMCUB-05-REGSEQ.md12276logstatsplainblame
-rw-r--r--AUDIT-DMCUB-06-USER-REACHABLE.md8716logstatsplainblame
-rw-r--r--AUDIT-DMCUB-07-DPAUX.md18220logstatsplainblame
-rw-r--r--README.md8311logstatsplainblame
-rw-r--r--dmcub_cfg.py8083logstatsplainblame
-rw-r--r--dmcub_cmdaudit.py28732logstatsplainblame
-rw-r--r--dmcub_copyaudit.py4624logstatsplainblame
-rw-r--r--dmcub_dis.py17326logstatsplainblame
-rw-r--r--dmcub_dpaux_audit.py6091logstatsplainblame
-rw-r--r--dmcub_emu_dpaux.py17320logstatsplainblame
-rw-r--r--dmcub_isa.py2442logstatsplainblame
-rw-r--r--dmcub_isa_evidence.py3722logstatsplainblame
-rw-r--r--dmcub_jxaudit.py4822logstatsplainblame
-rw-r--r--dmcub_map.py2640logstatsplainblame
-rw-r--r--dmcub_parse.py4431logstatsplainblame
-rw-r--r--dmcub_regmap.py5840logstatsplainblame
-rw-r--r--dmcub_regs.py8294logstatsplainblame
-rwxr-xr-xfetch_ref.sh2338logstatsplainblame
d---------fw495logstatsplain
d---------hw569logstatsplain

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.
  2. 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.
  3. AUDIT-DMCUB-02.md — dispatcher/register-map infrastructure, the address base, and the first finding.
  4. 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:

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).

./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.