# DF-3855 — VERDICT

## Status: `inconclusive` (parser desync proven deterministically by harness on a well-formed stream; kernel run hardware-gated)

## Full narrative

### The bug

`radeon_combios_external_tmds_setup()`, non-IGP branch
(`sys/dev/drm/radeon/radeon_combios.c:2948-2998`), walks the external-TMDS
init record stream:

```c
index = offset + 10;
id = RBIOS16(index);
while (id != 0xffff) {
        index += 2;                     /* skip the id word */
        switch (id >> 13) {
        case 0:
                reg = (id & 0x1fff) * 4;
                val = RBIOS32(index);
                WREG32(reg, val);       /* index NOT advanced past val */
                break;
        case 2: ... index += 4; ... index += 4; ...   /* advanced */
        case 4: ... index += 2; ...                   /* advanced */
        case 5: ... index += 4; ...                   /* advanced */
        case 6: ... index += 1; ...                   /* advanced */
        }
        id = RBIOS16(index);            /* re-reads the value low half */
}
```

Every case advances `index` past its operands **except case 0**. After a
plain register write the low 16 bits of the just-written *value* are
interpreted as the next command id, the intended `0xffff` terminator is
swallowed as operand bytes, and the walk continues past the end of the
record stream — executing data words (and, once past the table, whatever
bytes follow, i.e. the DF-1251 unbounded-walk family) as MMIO/PLL/I2C
commands.

The IGP branch of the same function advances correctly
(`radeon_combios.c:2900-2905`: `val = RBIOS32(index); index += 4; WREG32(...)`)
— the omission is plainly an oversight, also present upstream (verified
against Linux v5.10 radeon_combios.c:2965-2969).

### Caller / trigger

`radeon_legacy_encoders.c:985`, from the external-TMDS encoder mode-set
path — executes on every modeset on boards with an external TMDS transmitter
(sil164-class) whose VBIOS uses this table form (non-IGP).

### Impact assessment (honest)

Two scenarios:
1. **Malicious VBIOS**: the attacker already authors the command stream, so
   the desync adds nothing they don't already have (DF-1251 territory).
2. **Benign-but-corrupt / bit-flipped VBIOS**: a single flipped bit in a
   shift count or a truncated table turns a *valid* init sequence into
   unintended MMIO writes — register corruption on an otherwise healthy
   card, GPU hang class.

That bounds it at Low: real defect, marginal security delta, hardware-gated
(2 executors above), trivially fixable. The harness proves the misparse on a
well-formed stream (buggy walk: 2 writes, runs past terminator; fixed walk:
1 write, stops at terminator).

### Fix

`fix.diff`: add `index += 4;` after the case-0 value read (matches the IGP
branch). Apply-verified (`fix_apply.log`); runtime fix validation not
possible on this guest: `fix_status = not_testable`.

## kernel_refs
- sys/dev/drm/radeon/radeon_combios.c:2950-2959 (buggy case 0)
- sys/dev/drm/radeon/radeon_combios.c:2900-2905 (IGP branch, correct)
- sys/dev/drm/radeon/radeon_legacy_encoders.c:985 (caller)

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