# DF-5005 VERDICT — REPRODUCED (function-level, deterministic); firmware-gated producer

**Finding:** `amdgpu_atombios_get_clock_info()` (sys/dev/drm/amd/amdgpu/amdgpu_atombios.c:592-593)
copies `usReferenceClock` from the VBIOS FirmwareInfo table into
`ppll->reference_freq` (and clones it to all `ppll[1..3]` at :628-629) with
**no zero check**, unlike every sibling field in the same function that gets
an explicit `==0` fixup (`pll_out_min` :610-611, `lcd_pll_out_min/max`
:603-608, `spll->pll_out_min` :642-643, `mpll->pll_out_min` :669-670,
`max_pixel_clock` :708-709). This answers the rs400/r100 audit lead:
**yes, reference_freq can be 0 from the fw-info parse and it reaches
amdgpu_pll_compute** (atombios_crtc.c:835-854 picks `ppll[0..2]` and passes
the adjusted clock; amdgpu_pll.c:187 sets `den = pll->reference_freq`).

## Reproduction (guest)

Same harness as DF-5004 — verbatim copies of amdgpu_pll.c:44-251; the `ref0`
case sets `reference_freq = 0` with a normal 600 MHz-class mode clock
(freq=60000) and the stock amdgpu_atombios.c:618-625 PLL limits:

    case ref0 : freq=60000 ref=0 ... -> TRAPPED signal 8 (SIGFPE)
    case control (ref=2700)          -> COMPLETED dot=14850 fb=880 ref=2 post=80

gdb (`gdb_lines.txt`): `#0 amdgpu_pll_reduce_ratio (nom=..., den=...,
nom_min=4, den_min=11) at pll_harness.c:114` — `pll_harness.c:114` is the
verbatim `tmp = DIV_ROUND_UP(den_min, *den);` i.e. **amdgpu_pll.c:63**, and
`den_min=11` is `post_div_min = ceil(64800/6000) = 11`, proving the zero
divisor is the reference frequency (the mode clock is nonzero: 6000).

Determinism: 3/3 in the DF-5004 stress runs (`pll_harness ref0` exit 42
every time), plus the dedicated run in `run.log`.

## Why it panics

`den = 0` → `gcd(nom, 0) = nom` (sys/dev/drm/include/linux/gcd.h: `b == 0 →
return a`) → after the division `den = 0` survives → the denominator-minimum
fixup runs `DIV_ROUND_UP(den_min, 0)` → #DE trap → kernel panic at the first
display configuration after boot/resume. If the mode clock is *also* 0 the
first gcd itself is `gcd(0,0) = 0` and `*nom /= 0` traps at amdgpu_pll.c:51.
Sibling sink: atombios_crtc.c:875-879 divides the spread-spectrum step size
by `pll->reference_freq / 100`, so any firmware value `< 100` is a second
zero divisor on the same call chain.

## Impact / threat

The trigger is a corrupt or malicious VBIOS/ATOM image (or a code path that
leaves the field unset), **not** runtime attacker input — hence Low severity
(deterministic panic = firmware-robustness / supply-chain issue; no memory
corruption, no escalation). The consumer-side hardening is DF-5004's
fix.diff; the producer fix is this finding's fix.diff (same `==0` fixup
pattern the file already uses for its neighbours; 27 MHz default for
ppll/spll, 100 MHz for mpll).

## Fix validation

Producer-level: applying `fix.diff` replaces the 0 with 2700 — exactly the
`control` input, which completes with sane dividers (`run.log`). Consumer
guards (DF-5004 fix.diff) additionally make the unfixed producer complete
without trapping (see DF-5004 `fix_run.log`, `ref0` case). Kernel-level
rebuild validation not possible on this guest (no AMD GPU — `env.txt`).

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