# DF-4857 VERDICT

**Finding:** `do_div(tmp, reference_clock)` at `sys/dev/drm/radeon/rv730_dpm.c:71` divides by
`rdev->clock.spll.reference_freq`, which is copied verbatim from VBIOS
`FirmwareInfo.usReferenceClock` (`radeon_atombios.c:1189-1193`) with no zero
check. Crafted/corrupt VBIOS with `usReferenceClock==0` → fatal kernel trap
(divide error; on gcc 8.3 an `ud2` UB-guard → invalid opcode) on the DPM-enable
path for RV730/RV710.

**Status: REPRODUCED** (logic level, verbatim-replica harness on the audit
guest — the kernel-level trigger is hardware-gated: the guest has no Radeon
GPU, so `rv730_populate_sclk_value` cannot be entered in-kernel there; same
convention as DF-4492/DF-1992/DF-1937 for this driver family).

**Impact:** panic (pure DoS; no memory-corruption primitive — the divisor is
the only attacker input into the expression and the trap fires before any
memory write). No escalation chain exists for this bug class.

## How it was proven

1. **Reachability trace (source):** `radeon_device_init` → `radeon_pm_init`
   (dpm default on) → `radeon_dpm_enable` → `rv770_dpm_enable`
   (rv770_dpm.c:1956 `rv770_init_smc_table`; :1212 dispatch for RV730/RV710)
   and `rv770_convert_power_level_to_smc` (rv770_dpm.c:636) →
   `rv730_populate_sclk_value`. The division at :71 executes unconditionally
   for every engine-clock level — before the SS block at :91-107 (which is
   where DF-1937's ss.rate/clk_s divides live; DF-1937 explicitly does NOT
   cover :71).
2. **Unvalidated input (source):** `spll->reference_freq =
   le16_to_cpu(firmware_info->info.usReferenceClock)` (radeon_atombios.c:1192;
   DCE4 variant :1189-1190) — no zero check anywhere, while the *same parser*
   adds fallbacks for zero `pll_out_min` (:1173-1178, :1202-1207, :1229-1234),
   proving zero fields occur in real/corrupt images.
3. **Executable proof (guest):** `sclkdiv0.c` replicates rv730_dpm.c:40-117
   verbatim (register macros from rv730d.h:26-43, kernel-style `do_div`).
   - mode `ok`   (usReferenceClock=2700): completes, fbdiv=18204444 fits
     `SPLL_FB_DIV_MASK` — control.
   - mode `zero` (usReferenceClock=0): **fatal trap** — `Illegal instruction
     (core dumped)`, exit=132. Guest cc is gcc 8.3, which lowers the UB
     zero-division to `ud2` (confirmed via objdump in the transcript); a
     `divq` lowering yields #DE instead. In-kernel either is a hard panic
     (`Fatal trap 6/17 ... in kernel mode`).

## Fix validation

- `fix.diff` (guard `if (reference_clock == 0) return -EINVAL;` before :71 —
  identical in shape to the accepted DF-1992 fix for the rv740 twin) applied
  to the guest `/usr/src` together with DF-4858's fix; `radeon.ko` rebuilt
  **under -Werror**: compile+link OK (`radeon_module_build.log`,
  `radeon_fix_apply.log`).
- Harness with `-DFIX_DF4857`: mode `zero` returns `-EINVAL` cleanly, no
  division, exit=0 (`run.log`).
- Guest `/usr/src` reverted afterwards; `md5 rv730_dpm.c` verified back to the
  pristine `d993b8a12e7cc99729a7d82f9685be95`.

## Boundaries checked (no escalation)

- The only attacker-controlled value in the expression is the divisor; the
  quotient `fbdiv` is written into a fixed `RV770_SMC_SCLK_VALUE` struct
  (fixed-size fields, `cpu_to_be32`), not a variable-length buffer — no OOB.
- `fbdiv > SPLL_FB_DIV_MASK` (26 bits) from a crafted engine_clock would spill
  OR-bits into upper fields of `CG_SPLL_FUNC_CNTL_3` (register misprogram,
  hardware-functional only; upstream Linux identical) — hardening note, not a
  memory-safety bug; folded into the finding notes rather than filed
  separately.

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