# DF-3857 VERDICT — dce_v10_0_latency_watermark divide-by-zero (DF-3828 twin)

**Status: not_reproduced (kernel reach hardware-gated) — arithmetic primitive
proven on guest, source chain certain.**

## Root cause (path:line, all in sys/dev/drm/amd/amdgpu/ unless noted)

`dce_v10_0_latency_watermark()` divides by `available_bandwidth` at
`dce_v10_0.c:882-883` and by `wm->disp_clk` at `:884`, before the only guard
`if (wm->num_heads == 0) return 0;` at `:892`, with no divisor validation on
any caller path:

1. `dce_v10_0_crtc_do_set_base()` `:2064` → `dce_v10_0_bandwidth_update()`
   `:1145-1163` → `dce_v10_0_program_watermarks()` `:1013-1135` →
   `dce_v10_0_latency_watermark()` `:877`. Triggered by any mode set
   (unprivileged DRM master via `DRM_IOCTL_MODE_SETCRTC` / page-flip base
   update) and by `amdgpu_pm_compute_clocks` DPMS transitions (`:2510`).
2. `dce_v10_0_program_watermarks` `:1032-1040` / `:1071-1079`:
   with `adev->pm.dpm_enabled == false`, `wm.yclk = pm.current_mclk * 10`,
   `wm.sclk = pm.current_sclk * 10`.
3. `amdgpu_atombios_get_clock_info()` (`amdgpu_atombios.c:677-680`) copies
   VBIOS `ulDefaultEngineClock/ulDefaultMemoryClock` into
   `adev->clock.default_sclk/default_mclk` with **no zero-guard** (every
   neighbouring field does get one — see DF-3828's audit), and `:718-719`
   assigns them to `pm.current_sclk/current_mclk` even when the parse fails.
4. `dce_v10_0_available_bandwidth()` `:825-833` = `min(dram_bw, data_ret,
   dmif_req)`; `dram_bw = trunc(ch*4 * yclk/1000 * 0.7)` (`:707-725`) is 0
   when `yclk==0`; `data_ret` is 0 when `sclk==0` (`:765-783`). One zero
   term zeroes the `min`.
5. `:882` `(512*8*1000) / 0` → integer #DE → kernel panic.

Guard placement: the `num_heads==0` early-return at `:892` is dead through
this file's only caller (gate at `:1024` includes `num_heads`), so even a
perfect `num_heads` guard could not have prevented the zero-bandwidth divide —
the missing check is on the *divisors*, exactly the DF-3828/DF-3665 family
defect.

Secondary vectors examined and killed:
- `mode->clock == 0` (`div_u64` divides at `:1025-1027`, `wm->disp_clk` at
  `:884`): blocked pre-driver by `drm_mode_validate_basic()`
  (`sys/dev/drm/drm_modes.c:1114-1115`, `MODE_CLOCK_LOW`) in the
  `drm_mode_setcrtc`/`drm_mode_convert_umode` path (`drm_crtc.c:655`,
  `drm_modes.c:1750`). No user-supplied 0-clock mode reaches DCE10.
- `wm->src_width == 0` divide in `dce_v10_0_check_latency_hiding()` `:978`:
  same validation rejects `hdisplay==0` (`drm_modes.c:1117`).
- HPD/audio/crtc array bounds (dce_v11 template): `hpd_offsets[6]` uses are
  guarded (`:279`, `:303`, `:331`, `:383`, `:3010`, `:3162`);
  `interrupt_status_offsets[6]` / `crtc_offsets[7]` uses
  (`:3206-3207`, `:3113-3114`) are bounded by the amdgpu_irq dispatch
  contract (only client/src_id pairs registered via `amdgpu_irq_add_id`
  reach `.process`; `amdgpu_irq.c:356`, `:414-432`); `num_pins=7`/`num_dig=7`
  fit `pin[AMDGPU_MAX_AFMT_BLOCKS=9]`/`afmt[9]` (`amdgpu_mode.h:261,329`);
  `crtc_status[6]` (`:403`) fits fixed `num_crtc=6` (`:452-465`).
  The read-before-check at `dce_v10_0_pageflip_irq` `:3114` vs `:3116` is
  latent-only (registered pageflip src_ids 8..18 → crtc_id 0..5).
- `dce_v10_0_cursor_set2` width==0 → `CUR_SIZE` gets 0xFFFFFFFF (`:2330`):
  register cosmetics only, no memory-safety impact.

## What ran on the guest

`wm_div0.c` replicates the exact fixed-point truncation sequence of
`dce_v10_0_dram_bandwidth/data_return_bandwidth/dmif_request_bandwidth/
available_bandwidth/latency_watermark` with argv-controlled clocks (so the
compiler cannot fold the divide).

Results (build `cc 8.3 [DragonFly]`, `-O2 -Wall`):
- Zero-clock input: **SIGFPE, exit=136** — 3/3 runs (`run.log` ×2,
  `run.2.log`), dmesg: `pid NNNN (wm_div0), uid 0: exited on signal 8`.
- Healthy-clock control (6000000/3000000 kHz): returns 2297, no trap.

Signal 8 in userspace is the same #DE fault the kernel takes on the same
instruction; in-kernel this is an unrecoverable panic (kernel #DE has no
fixup here).

## Why not `reproduced`

The audit guest is QEMU/KVM with virtio + bochs VGA: `pciconf -lv` lists
**0** devices with `vendor=0x1002` and there is no `/dev/dri` (`env.txt`), so
`amdgpu.ko` never attaches and the DCE10 kernel path cannot execute. Kernel
reach requires a Tonga/Fiji dGPU in the affected state (zero default clocks
from VBIOS with DPM off). Impact ceiling on equipped hardware: **local DoS
via panic**, triggered by an unprivileged DRM client's mode set.

## Fix validation

`fix.diff` guards the three divides (bail to the `num_heads==0`-style 0
return), adds the `available_bandwidth==0` check to the sibling callers, and
adds zero-guards for `default_sclk/default_mclk` in
`amdgpu_atombios_get_clock_info` (upstream-root fix mirroring DF-3828).
A patched-kernel build cannot be exercised on this guest (no AMD ASIC ⇒
amdgpu never binds ⇒ no observable behavior difference), so fix validation is
**not_testable** here; the diff is git-apply-clean against the read-only
`sys/` tree.

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