# DF-3853 — VERDICT

## Status: `inconclusive` (arithmetic proven identical to kernel code; harness SIGFPE == kernel #DE == panic; no radeon hardware on audit guest)

## Full narrative

### Where the bug is

`radeon_combios_get_clock_info()` (`sys/dev/drm/radeon/radeon_combios.c:725-784`)
loads PLL reference frequencies **straight from the VBIOS with no validation**:

- `radeon_combios.c:741`  `p1pll->reference_freq = RBIOS16(pll_info + 0xe);`
- `radeon_combios.c:758`  `spll->reference_freq = RBIOS16(pll_info + 0x1a);`
- `radeon_combios.c:773`  `mpll->reference_freq = RBIOS16(pll_info + 0x26);`

The only post-parse sanitization anywhere is in `radeon_get_clock_info()`
(`radeon_clocks.c:194-216`), and it clamps **reference_div only**
(`if (spll->reference_div < 2) ...`); `reference_freq` is never checked —
note that the *fallback* path in the same function knows good defaults
(`radeon_clocks.c:229+`: IGP 1432 / discrete 2700), the parse path just
doesn't apply them.

### The sink

`calc_eng_mem_clock()` (`sys/dev/drm/radeon/radeon_clocks.c:344-385`) divides
by the BIOS-supplied sys-clock reference frequency, unguarded:

```c
int ref_div = spll->reference_div;
if (!ref_div) ref_div = RREG32_PLL(...) & MASK;      /* ref_div is handled */
...
req_clock /= (2 * spll->reference_freq);             /* :377 — NOT handled */
```

`ref_div` got a zero-guard; `reference_freq` did not. A crafted COMBIOS PLL
table with the word at `pll_info+0x1a` equal to zero makes this a 32-bit
integer division by zero: #DE on x86 → `divide_error` → **kernel panic**.
(Second, weaker order: `req_clock /= ref_div` at :382 if the register
fallback also reads 0.)

### Reachability

- Parse: `radeon_clocks.c:186-189` — COMBIOS-format image (pre-AVIVO chips;
  AVIVO chips reject non-atom BIOSes, which is why the
  `radeon_compute_pll_avivo` divide path is *not* reachable from this parser).
- Execution: PM clock switch — `radeon_pm.c:224 radeon_set_engine_clock()` →
  `radeon_legacy_set_engine_clock` (registered for r100..rv4xx/rs4xx,
  radeon_asic.c:253,321,417,485,621) → `calc_eng_mem_clock`.
  Triggers: `radeon_pm_set_clocks()` from profile/dynpm transitions, resume
  (`radeon_pm.c:507`), and the initial `radeon_pm_compute_clocks()` at load
  (`radeon_pm.c:464`) — the latter requires ≥2 power states, which the same
  crafted BIOS provides via the combios POWERPLAY table on a mobility board.
- Attacker model: same crafted-VBIOS precondition as DF-1250/1251/1252
  (malicious card / flashed ROM / VFCT-injected image). Impact once the image
  is in place: **local DoS (kernel panic), repeatedly, at boot/PM events** —
  no user privilege needed after the hardware is in the machine.

### Why the harness is faithful

`harness.c` copies the kernel statements verbatim (commented with
path:line). The only stub is `RREG32_PLL()`, stubbed to return a plausible
nonzero divider so that the single zero denominator is exactly the
BIOS-controlled `reference_freq`. Userspace `uint32 /= 0` raises SIGFPE —
the same fault class the kernel would take as #DE.

### Why `inconclusive`

The audit guest is virtio-only; no radeon device → the kernel path cannot
run there. Source-level certainty is complete: every statement between the
BIOS byte and the division is cited and replicated.

### Fix

`fix.diff` (two files):
1. `radeon_combios.c`: after the mpll block, sanitize zero reference
   frequencies to the known-good defaults (2700) for p1/spll/mpll.
2. `radeon_clocks.c` (defense in depth): guard `!spll->reference_freq` and
   `!ref_div` inside `calc_eng_mem_clock()` before either division.

Apply-verified against the pristine tree (`patch --dry-run` clean,
`fix_apply.log`). Runtime fix validation impossible on this guest:
`fix_status = not_testable`.

## kernel_refs
- sys/dev/drm/radeon/radeon_combios.c:741,758,773 (unchecked loads)
- sys/dev/drm/radeon/radeon_clocks.c:186-189 (dispatch)
- sys/dev/drm/radeon/radeon_clocks.c:194-216 (clamp block — div only)
- sys/dev/drm/radeon/radeon_clocks.c:344-385 (calc_eng_mem_clock, :377 divide)
- sys/dev/drm/radeon/radeon_pm.c:224,464,507 (triggers)
- sys/dev/drm/radeon/radeon_asic.c:253 (asic callback)

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