# DF-4932 VERDICT

**Status: reproduced (logic level, verbatim userland replica on the audit
guest). Kernel trigger hardware-gated (no AMD GPU — 0 PCI 0x1002 devices) and —
importantly — caller-gated/latent: no in-tree caller of `dc_interrupt_set()`/
`dc_interrupt_ack()` passes a `DC_IRQ_SOURCE_DC*_VLINE0` source on a dce110
service today. The defect is proven; the panic requires a future/alternate
caller.**

## The defect

`enum dc_irq_source` (`sys/dev/drm/amd/display/dc/irq_types.h:40-148`) runs to
`DAL_IRQ_SOURCES_NUMBER = 90` and includes `DC_IRQ_SOURCE_DC1_VLINE0 ..
DC_IRQ_SOURCE_DC6_VLINE0` = **84..89** (VBLANK1..6 = 78..83).

`irq_source_info_dce110[DAL_IRQ_SOURCES_NUMBER]`
(`irq_service_dce110.c:226-313`) is initialized with designated initializers
covering exactly enum values 0..83 (INVALID, HPD, HPDRX, I2C_DDC, DPSINK,
TIMER, PFLIP, PFLIP_UNDERLAY0, GPIOPAD0-30, DC1-6UNDERFLOW, DMCU_SCP, VBIOS_SW,
VUPDATE1-6, VBLANK1-6). **Entries 84..89 (the VLINE0 block) receive no
initializer** — they stay zero: `enable_reg=0 … .funcs=NULL`.

The generic dispatch only bounds-checks the index:

- `find_irq_source_info()` — `irq_service.c:82`:
  `if (source >= DAL_IRQ_SOURCES_NUMBER || source < DC_IRQ_SOURCE_INVALID)
  return NULL;` — 84..89 pass.
- `dal_irq_service_ack()` — `irq_service.c:152`:
  `if (info->funcs->ack) …` — reads `*(NULL + offsetof(ack))` ⇒ fault.
- `dal_irq_service_set()` — `irq_service.c:116,118`: acks first (same deref),
  then `if (info->funcs->set)`.

This is the same NULL-funcs-dispatch shape as the reproduced DF-4713 family
(amdgpu_irq.c), here rooted in this file's incomplete table rather than an
unregistered DRM-level source.

## Reachability audit (why "latent")

Every in-tree path into the service was traced:

- `dc_interrupt_set/ack` (`core/dc.c:1681-1693`) do no source validation beyond
  the generic index check.
- Callers: `dm_irq_state` (amdgpu_dm_irq.c:520-556) passes
  `IRQ_TYPE_PFLIP/IRQ_TYPE_VBLANK + acrtc->otg_inst` with `otg_inst` from
  `status->primary_otg_inst = pipe_ctx->stream_res.tg->inst`
  (amdgpu_dm.c:4856, dc_resource.c:2055) — hardware-bounded 0..5 on dce110;
  `amdgpu_dm_hpd_init/fini` pass `dc_link->irq_source_hpd{,_rx}` (HPD/HPDRX);
  `amdgpu_dm.c:3014` passes `IRQ_TYPE_VBLANK + otg_inst` (same bound).
- The interrupt *arrival* path (`amdgpu_dm_irq_handler` →
  `dc_interrupt_to_irq_source` → `to_dal_irq_source_dce110`) can only return
  {INVALID, VBLANK1-6, VUPDATE1-6, PFLIP1-6, HPD1-6, HPD RX1-6} — all covered;
  INVALID maps to the dummy entry.

So nothing passes 84..89 into a dce110 service in this tree. The gap becomes a
panic the moment any code (a DCN-port, a DM change, or
`DAL_VALID_IRQ_SRC_NUM`'s own off-by-one — `irq_types.h:158` allows
`src == DAL_IRQ_SOURCES_NUMBER`, see notes) starts forwarding VLINE0 or
edge-of-enum sources. dce120/dce80 tables have the identical shape (context
note for the maintainers).

## Reproduction (run.log / run.2.log, audit guest, cc 8.3)

- Table walk: exactly `table[84] … table[89] (.funcs == NULL)`; 6 gaps.
- Control: index 90 rejected; `PFLIP1` enable works via the generic RMW path.
- Trigger: `dal_irq_service_set(DC_IRQ_SOURCE_DC1_VLINE0=84)` crashes inside
  `dal_irq_service_ack` at the `info->funcs->ack` load — SIGSEGV exit 139,
  identical across two runs.
- Fixed: dummy entries + `!info->funcs` guards → `dal_irq_service_dummy_ack/set`
  log and return false; exit 0.

## Impact ceiling

A NULL-dereference in kernel context ⇒ hard panic (local DoS) on the machine
owning the display. No write primitive, no info leak, no escalation chain. Not
triggerable by an unprivileged local user through any existing path.

## Fix validation

`fix.diff` = six `dummy_irq_entry()` rows for `DC_IRQ_SOURCE_DC{1..6}_VLINE0` in
the dce110 table + `if (!info->funcs) {DC_LOG_ERROR; return false;}` guards in
`dal_irq_service_set`/`dal_irq_service_ack` (protects every asic table against
any future gap, incl. dce120/dce80). Applied to the guest's `/usr/src` together
with DF-4933's diff (patched md5s `724635b5…`/`305f0881…` vs pristine
`95266b5b…`/`35607b1a…` — the first build attempt accidentally skipped the
irq_service.c hunks (interactive patch on an already-patched tree); redone from
pristine with both files verified patched *at build time*). `amdgpu.ko` rebuilt
under `-Werror`: RC=0, zero errors, `irq_service.o irq_service_dce110.o` linked
(`amdgpu_module_build.log`). Harness fixed mode: no crash, graceful dummy
handling. Guest sources reverted md5-verified pristine.

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