# DF-4985 — dce110 command-table helper: five NULL vtable members vs unguarded consumer call sites (latent NULL-func-call under ASIC/VBIOS generation mismatch); DF-2030 family attribution completed

**File:** `sys/dev/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c` (364 LOC)
**Severity:** Info (hardware-gated latent; unreachable on genuine hardware) · **Confidence:** certain (dead shapes), likely (mismatch reachability)
**Pass 2 (GLM 5.3)** · known pass-1 finding on this file: DF-2030 (dead `return 0` in `dig_encoder_sel_to_atom`, lines 154-190) — **not re-reported here**
**Verdict:** `untested` (Info hardening; guest has no AMD GPU — amdgpu never attaches; no hardware path exists on the audit guest)

## Liveness (who consumes this table)

- Compiled into the `amdgpu` kmod: `sys/dev/drm/amd/amdgpu/Makefile:217` (`SRCS+= command_table_helper_dce110.c`).
- Selected for **DCE_VERSION_10_0** (Tonga/Fiji) and **DCE_VERSION_11_0** (Carrizo) — `sys/dev/drm/amd/display/dc/core/dc_resource.c:68,72-75`.
- Via **both** parsers: v1 `command_table_helper.c:46,50` (old-VBIOS fallback) and v2
  `command_table_helper2.c:47,51` (firmware parser, selected first by
  `bios_parser_interface.c:41-45`; `bios_parser2.c` requires ROM header 2.2+ / object info 1.4+).

## The latent matrix

`command_table_helper_dce110.c:332-349` leaves **five** members of
`struct command_table_helper` (`command_table_helper_struct.h:34-64`) NULL:

| member | dce110 | dce80 | dce112 | unguarded consumer |
|---|---|---|---|---|
| `assign_control_parameter` | **NULL** (:344) | impl | — | `command_table.c:204,219` (`encoder_control_dig1_v1/dig2_v1`) |
| `clock_source_id_to_ref_clk_src` | **NULL** (:345) | impl | — | `command_table.c:678` (`transmitter_control_v4`) |
| `transmitter_bp_to_atom` | **NULL** (:346) | impl | — | `command_table.c:507,638,764` (`transmitter_control_v2/v3/v4`) |
| `dc_clock_type_to_atom` | **NULL** (implicit) | — | impl | `command_table.c:2437` and `command_table2.c:722` (`set_dce_clock_v2_1`) |
| `transmitter_color_depth_to_atom` | **NULL** (implicit) | — | impl | (dce112+ paths only) |

None of the six call sites NULL-checks the function pointer before calling → a
generation-mismatched pairing (DCE10/11 ASIC + VBIOS exposing rev-2/3/4 transmitter tables,
rev<3 DIG encoder tables, or a SetDCEClock table) would NULL-func-call → panic in display init.

## Why it is unreachable on genuine hardware (the version matrix is coherent by design)

- The rev-2/3/4 transmitter handlers that use `transmitter_bp_to_atom` /
  `clock_source_id_to_ref_clk_src` bind only from `init_transmitter_control`
  (`command_table.c:367-396`) when the card's VBIOS `UNIPHYTransmitterControl` crev is 2/3/4 —
  DCE8-generation firmware, which pairs with the **dce80** table (implements both).
  DCE10/11 firmware uses crev 5/6 → `transmitter_control_v1_5/v1_6`, which use only
  implemented members (`phy_id_to_atom`, `hpd_sel_to_atom`, `dig_encoder_sel_to_atom`,
  `clock_source_id_to_atom`).
- Same logic for `assign_control_parameter`: `dig1/dig2_v1` bind for VBIOS DIG encoder control
  rev < 3 (DCE8-era); DCE10/11 use `encoder_control_digx_v3`+.
- `set_dce_clock` (`command_table.c:2437` / `command_table2.c:722`) is reached only from
  `dce112_set_clock` (`dce/dce_clocks.c:292-340`), which is wired **exclusively** into
  `dce112_funcs`/`dce120_funcs`/`dcn1_funcs` (`dce_clocks.c:745,752,758`). DCE10/11 resources
  wire `dce110_funcs` (`dce_clocks.c:762-767`, `set_dispclk = dce_psr_set_clock` →
  `dce_set_clock` → `program_display_engine_pll`, **never** `set_dce_clock`).
  DCE11.2+ (which does call it) uses the dce112 table, which implements `dc_clock_type_to_atom`.
- The parser wrappers guard only their own `cmd_tbl.set_dce_clock` NULL
  (`bios_parser.c:778`, `bios_parser2.c:1040`), not the helper member — irrelevant given the above.

Residual trigger: a transplant/mismatched VBIOS (DCE10/11 card flashed with DCE8-era command
tables, or a DCE10/11-class card whose dccg funcs got swapped) — i.e., an attacker who already
controls the machine's firmware/hardware. Out of the local/remote attacker bar.

## Family attribution checks requested by the orchestrator

1. **DF-2030 attribution:** confirmed filed on **this file** (lines 154-189, `return 0`
   discarding the computed DIGx_SEL mapping) — pass 1, not re-reported. Callers
   `command_table.c:798,855`, `command_table2.c:213` feed the always-0 into AtomBIOS params
   (uint8_t field; no memory-safety consequence; upstream-intended per DF-4934 analysis).
2. **DF-4935 shape (`dc_clock_type_to_atom` uninit-output):** does **NOT** exist in dce110 —
   this file has no `dc_clock_type_to_atom` implementation at all (implicit NULL vs dce112's
   implemented-but-uninit-in-default). Distinct shape, filed here as the NULL-member matrix.
3. **`command_table_helper2_dce110`?** Does not exist — there is only the single v1-style
   `command_table_helper_dce110.c` shared by both parsers (dce112 is the only family with
   separate v1/v2 files; dce80 is v1-only).
4. **Other switches audited clean:** `phy_id_to_atom` (default→UNIPHYA), `signal_type_to_atom_dig_mode`
   (default→DVI), `clock_source_id_to_atom_phy_clk_src_id` (default→P1PLL), `hpd_sel_to_atom`
   (default→0) all initialize their outputs on every path. `clock_source_id_to_atom`
   (:192-236) returns `true` without writing only when `atom_pll_id == NULL` — all callers pass
   valid stack pointers. `engine_bp_to_atom` (:238-281) omits ENGINE_ID_DACB/VGA but returns
   `false`, and both callers (`command_table.c:1951,1990`, `command_table2.c:509`) treat
   `false` as `BP_RESULT_FAILURE`. No uninitialized-output feed exists in this file.

## Recommended fix

Defense-in-depth only: either implement the three v2-vintage members by reusing the shared
`dal_cmd_table_helper_*` implementations (as dce80 does) and add a `dc_clock_type_to_atom`
that mirrors dce112's with an initialized default, or add `if (!cmd->member) return
BP_RESULT_BADINPUT;` guards at the six call sites. Upstream parity is preserved either way.

## Reproduce

Not reproducible on the audit guest (no AMD GPU; amdgpu cannot attach). Static reproduction of
the matrix: see `null_matrix_proof.log`.
