# DF-5079 VERDICT — dce120 vupdate entries (no .src_id) → dce110_vblank_set pipe_ctx[178] OOB, replica-reproduced

**Status: reproduced (logic level, family-standard replica), impact panic,
confidence certain.** DF-4933 (dce110) / DF-5055 (dce80) twin wired
independently by the DCE120 table's own macros.

## Root cause (code-certain)

* `vupdate_int_entry` (irq_service_dce120.c:138-144) initializes
  `IRQ_REG_ENTRY(...)` + `.funcs = &vblank_irq_info_funcs` — and **no
  `.src_id`/`.ext_id`** (struct irq_service.h:46-57 has both fields; the
  entries therefore carry `{src_id=0, ext_id=0}`).
* `vblank_irq_info_funcs` (irq_service_dce120.c:82-85) wires
  `.set = dce110_vblank_set`.
* `dce110_vblank_set` (irq_service_dce110.c:195-219) re-derives the source
  from the table entry:
  `dc_interrupt_to_irq_source(dc, info->src_id=0, info->ext_id=0)`
  → `to_dal_irq_source_dce110(0,0)` (dce110:315-393) hits the default case
  → `DC_IRQ_SOURCE_INVALID (0)`;
  then `uint8_t pipe_offset = dal_irq_src - IRQ_TYPE_VBLANK` (dce110:205) =
  `(uint8_t)(0 - 78)` = **178**;
  then `core_dc->current_state->res_ctx.pipe_ctx[178].stream_res.tg`
  (dce110:207-208) — a **far-OOB read on the MAX_PIPES=6 array**
  (core_types.h:239-241, hw_shared.h:37) — and the very next statement
  calls through the garbage pointer: `tg->funcs->arm_vert_intr(tg, 2)`
  (dce110:210-211).
* Double-baked upstream: even a correctly remapped VUPDATE source
  underflows (VUPDATE1=72 − 78 → 250), because the subtraction base is
  IRQ_TYPE_VBLANK, not IRQ_TYPE_VUPDATE (printed by the replica).
* For contrast, the dce120 vblank entries DO set
  `.src_id = VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 + reg_num` = 19..24
  (consecutive, verified against ivsrcid_vislands30.h:66-156), so the
  in-tree-reachable VBLANK path arms pipes 0..5 correctly — the defect is
  confined to the VUPDATE wiring (whose true src ids are the non-consecutive
  7,9,11,13,15,17, ivsrcid_vislands30.h:30-61).

## Reproduction (this run, on the audit guest)

Verbatim userland replica (dce120_vupd.c, adapted from DF-5055's harness):
dce120 vupdate/vblank macros + the dce120-local vblank funcs table,
dce110_vblank_set, to_dal_irq_source_dce110, the set dispatch
(irq_service.c:77-124), and the dc→current_state→res_ctx.pipe_ctx[] walk
against a 0x5a-poisoned 8MiB mapping with valid timing generators in pipes
0..5. Results (run.log, run.2.log — deterministic x2):

* table: `vupdate1 src_id=0 ext_id=0 | vblank1 src_id=19`
* double-baked: `(uint8_t)(72-78) = 250`
* controls: VBLANK1..6 arm pipes 0..5 through the REAL conversion path
* trigger: `set(DC_IRQ_SOURCE_VUPDATE1)` → pipe_ctx[178] → garbage tg →
  wild `arm_vert_intr` → **SIGBUS exit 138**
* fixed mode (fix.diff): index-derived pipe — all 12 VUPDATE/VBLANK sources
  arm pipes 0..5, **exit 0**

Kernel console corroborates (dmesg.txt: `pid ... (dce120_vupd_base) ...
exited on signal 10` x2 per run).

## Reachability (traced honestly — latent)

No in-tree caller ever `.set`s a VUPDATE source: suspend/resume touch
HPD/HPDRX only (amdgpu_dm_irq.c:353/380/406, 625-645); vblank enable is
`IRQ_TYPE_VBLANK + otg_inst` (amdgpu_dm.c:3014-3015); pflip is
IRQ_TYPE_PFLIP (amdgpu_dm_irq.c:535-556); the IH converter returns VUPDATE
only for src ids 7,9..17 which amdgpu_dm never registers. Service exists
only on DCE12 hardware (dce120_resource.c:1011); guest has no AMD GPU.

## Ceiling

Far-OOB read feeding a wild indirect call — a groomed heap around the
dc_state allocation could in principle aim `tg` (not demonstrated; honest
ceiling is a kernel panic, family-consistent with DF-5055).

## Fix validation

fix.diff (two hunks): (1) dce120 `vupdate_int_entry` gains
`.src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + 2 * reg_num` (7,9,..,17);
(2) `dce110_vblank_set` derives the pipe from the TABLE INDEX via the
`vblank_pipe_offset()` helper — provably 0..5 for both ranges, the exact
DF-4933/DF-5055 fix shape (the dce110 hunk is byte-identical to DF-5055's,
the shared-function owner). Applied to guest /usr/src together with
DF-5078's diff: hunks succeeded at :192/:226 (dce110) and :140 (dce120);
full `make -j6` amdgpu.ko rebuild **rc=0, 0/0 under -Werror**
(amdgpu_module_build.log; irq_service_dce110.c compiled at :572);
replica fixed mode exit 0 with all 12 sources arming the correct pipes;
sources reverted md5-verified pristine (35607b1a.../51bd6ea8...),
`make clean`. Guest returned to clean snapshot afterwards.

## Artifacts

dce120_vupd.c (code_hash 44d0edf9...), build.log, run.log, run.2.log,
dmesg.txt, env.txt, fix.diff, amdgpu_module_build.log, fix_build.log,
fix_run.log, manifest.json, verdict.json.

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