# DF-5000 VERDICT — dce80 helper leaves 2/17 vtable members NULL; the SetPixelClock-V7 rev handlers call `transmitter_color_depth_to_atom` unguarded

**Status: untested** (Info severity, hardware-gated). The kernel path requires AMD DCE8
silicon (Sea Islands: Bonaire/Kaveri/Kabini/Mullins) paired with a VBIOS whose
SetPixelClock command-table parameter revision is 7 — a cross-generation pairing. The
audit QEMU guest has no AMD GPU at all (`env.txt`: `NO_AMD_GPU`), so amdgpu never
attaches and the path cannot be entered, let alone faulted. All structural claims below
are proven statically in this tree (`null_member_matrix.log`), and the C semantics
(designated-initializer zero-fill + unguarded indirect call ⇒ fault at member offset)
were demonstrated as a userspace harness **on the DragonFly guest** (`run.log`,
exit 42). Line references verified 2026-09-25.

## 1. What the file is and who reaches it

`command_table_helper_dce80.c` builds the single shared `struct command_table_helper`
vtable (`:329-349`) returned by `dal_cmd_tbl_helper_dce80_get_table()` (`:351-354`). It
is compiled into the amdgpu kmod (`sys/dev/drm/amd/amdgpu/Makefile:214`) and is selected
for DCE_VERSION_8_0/8_1/8_3 (`core/dc_resource.c:57,63,65`) by **both** parser
dispatchers: v1 `command_table_helper.c:39-43` and v2 `command_table_helper2.c:40-44`.
Parser choice is `firmware_parser_create` first, v1 fallback
(`bios_parser_interface.c:41-45`); both construct for DCE8 and both ignore the
dispatcher's return value (`bios_parser2.c:1987`, `bios_parser.c:2936`).

## 2. The NULL members (proven)

The struct declares 17 members (`command_table_helper_struct.h:33-63`). The dce80
initializer lists 15 designators (`:330-348`). Missing:

| member | consumer call sites | guard at call site |
|---|---|---|
| `transmitter_color_depth_to_atom` | `command_table.c:1188` (v1), `command_table2.c:308-311` (v2) — both inside `set_pixel_clock_v7` | **none** |
| `dc_clock_type_to_atom` | `command_table.c:2437` (v1), `command_table2.c:722` (v2) — inside `set_dce_clock_v1` | bool-return checked, but the **call itself** goes through the pointer |

C99 6.7.8p10 zero-fills unnamed members of a designated-initializer aggregate ⇒ both are
NULL. Contrast: dce112 — the only table of the DCE11.2 (Polaris) generation that V7
pixel-clock ROMs genuinely pair with — implements both
(`command_table_helper_dce112.c:358-376,401-402`); dce110 also leaves both NULL (counted
in DF-4985's five, but with **no call site listed for them** there).

## 3. The panic chain (transmitter_color_depth_to_atom)

1. DCE8 ASIC, amdgpu DC attaches; dce80 helper selected.
2. ROM reports SetPixelClock parameter revision **7** ⇒ `init_set_pixel_clock` wires
   `set_pixel_clock_v7` (`command_table.c:930-931`; v2 twin `command_table2.c:247-248`).
   Other revisions wire v3/5/6 or leave NULL (graceful `BP_RESULT_FAILURE` via
   `bios_parser.c:766-767`).
3. First display enable / mode set ⇒ `bios_parser_set_pixel_clock` ⇒ `set_pixel_clock_v7`.
4. `:1159` `clock_source_id_to_atom` (implemented, returns true), `:1160`
   `controller_id_to_atom` (implemented) ⇒ body entered.
5. `:1188` `bp->cmd_helper->transmitter_color_depth_to_atom(color_depth)` — call through
   NULL+0x80 ⇒ immediate kernel page fault / panic in display bring-up. No check exists
   anywhere between the dispatcher and the call.

## 4. Why genuine hardware is safe (and what the real trigger is)

Genuine DCE8 VBIOSes are SetPixelClock **V6**: radeon drives the same Sea Islands ROMs
(the same AtomBIOS command table) with a V5/V6-only implementation
(`sys/dev/drm/radeon/atombios_crtc.c:760-766` union, `:916` `case 6:`), and has no V7
path at all — if any genuine DCE8 ROM reported rev 7, radeon CIK modesetting would have
no handler and would fail visibly. The V7 parameter revision belongs to the
DCE11.2+ (Polaris/DCE12) ROM generation, whose ASICs select the dce112 table that
implements the member. Hence:

- **Genuine pairing:** DCE8 ROM (V6) → `set_pixel_clock_v6` (`command_table.c:1070-1146`)
  never touches the member. No crash — consistent with CIK DC working upstream.
- **Attack pairing:** DCE8 silicon + ROM built for a V7 generation (physical reflash of
  the card's own VBIOS, or a malicious/crafted add-in card / eGPU ROM). Then step 2-5
  above fire on the first mode set.

`dc_clock_type_to_atom` is triply dead on DCE8: (a) its only DC-side callers
(`dce/dce_clocks.c:298-334`) are constructed solely for dce110/112/120/dcn10
display-clock managers — `dce80_resource.c` builds no such clock manager; (b) the
`set_dce_clock` rev handler is wired only if the ROM carries the SetDCEClock command
(DCE11.2-era); (c) `bios_parser_set_dce_clock` NULL-checks the rev handler
(`bios_parser.c:778-779`). It is fixed by the same vtable completion for
defense-in-depth.

## 5. What is NOT wrong here (negative results, pass-2 depth)

- `dig_encoder_sel_to_atom` (`:239-271`): default maps to `DIGA_SEL` and the function
  returns the computed variable — the DF-2030/DF-4934/DF-4936 dead-return-0 shape does
  **not** exist in dce80.
- `engine_bp_to_atom` (`:61-104`) / `clock_source_id_to_atom` (`:106-150`): default
  leaves the output unwritten but returns **false**, and every consumer checks the bool
  before use (`command_table.c:1159,1951-1955,1990-1994`; `command_table2.c:271-273,509`)
  — the DF-4935 uninit-output shape does not exist here (unlike dce112's
  `dc_clock_type_to_atom`, dce80's clock_source function correctly returns false).
- `encoder_action_to_atom` / `disp_power_gating_action_to_atom` defaults →
  BREAK_TO_DEBUGGER + 0: byte-identical family shape (dce110 `:300-302` etc.), not a
  per-file defect.
- `clock_source_id_to_atom_phy_clk_src_id` `>>2` (`:175`) and `hpd_sel_to_atom` `>>4`
  (`:236`): family-identical (dce110 `:120,151`; dce112 `:117`); constants sane
  (`ATOM_TRANSMITTER_CONFIG_V5_P0PLL=0x08`, `HPD1_SEL=0x10`, atombios.h:1543,1550).
- All 5 shared `dal_cmd_table_helper_*` bindings resolve to the shared
  `command_table_helper.c` implementations (pass-1 audited clean).

## 6. Family attribution

- DF-4985 (dce110, pass-2): same family; its five-NULL-member count *included* these two
  members but its six-site unguarded-caller matrix **omitted** the `set_pixel_clock_v7`
  sites (`command_table.c:1188`, `command_table2.c:308-311`) — this finding completes
  that matrix and corrects its premise that "the dce80 table implementing them" covers
  everything dce110 NULLs (true only for the three explicitly-NULLed members).
- DF-2030/DF-4934/DF-4936 (dead return-0): shape absent in dce80 (see §5).
- DF-4935/DF-4937 (uninit output + return true): shape absent in dce80 (§5); note the
  copy in `fix.diff` deliberately fixes that defect in the implanted
  `dc_clock_type_to_atom` (default → output written, `ret_code = false`).

## 7. Fix

`fix.diff` (git-apply-able, `git apply -p0`, check-verified against the pristine tree):
implant `transmitter_color_depth_to_atom` (dce112 `:358-376` semantics, dce80's
BREAK_TO_DEBUGGER style) and `dc_clock_type_to_atom` (dce112 `:333-355` semantics with
the DF-4935 defect fixed) and bind both in the vtable. After the fix no dce80 member is
NULL and the four consumer call sites are unconditionally safe. Fix validation on
hardware is impossible on this guest (`fix_status: not_testable`).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
