# DF-3828 VERDICT

## Bottom line

**Source-certain divide-by-zero family in the DCE11 watermark math;
arithmetic-level reproduction on the guest succeeded (SIGFPE = the exact
#DE fault class, 2/2 runs); kernel-path reproduction impossible here (no
AMD ASIC). Verdict `not_reproduced` for the kernel reach with the primitive
proven and the trigger chain fully traced.**

## The defect

`dce_v11_0_latency_watermark()` — `sys/dev/drm/amd/amdgpu/dce_v11_0.c`:

- :908 `(512 * 8 * 1000) / available_bandwidth` — divides by
  `dce_v11_0_available_bandwidth(wm)` (:851-859) = `min(dram_bw,
  data_return_bw, dmif_bw)`; each term is a fixed-point product that
  truncates to **0** when its clock input is 0.
- :909 `(128 * 4 * 1000) / available_bandwidth` — same divisor.
- :910 `40000000 / wm->disp_clk` — `wm->disp_clk = mode->clock`
  (program_watermarks :1068).
- :918 `if (wm->num_heads == 0) return 0;` — the only guard, placed *after*
  the divides (mirrors the DF-3665 evergreen pattern).

No caller validates the divisors: `dce_v11_0_program_watermarks` (:1085,
:1124) guards only `enabled && num_heads && mode` (:1050).

## Zero-clock provenance (kernel refs)

- `dce_v11_0_program_watermarks:1064-1065` (DPM-disabled path):
  `wm.yclk = adev->pm.current_mclk * 10; wm.sclk = adev->pm.current_sclk * 10`.
- `amdgpu_atombios_get_clock_info` — `sys/dev/drm/amd/amdgpu/amdgpu_atombios.c`:
  `default_sclk/default_mclk` taken verbatim from the VBIOS firmware-info
  table at :677-680 **without the zero-guard that every neighboring clock
  field gets** (:610, :642, :669, :694, :708), and unconditionally assigned
  to `pm.current_sclk/current_mclk` at :718-719 **even when the table parse
  failed** (`ret != 0`), leaving both 0.
- Consequence: on a board whose VBIOS reports zero default engine/memory
  clocks (or after a failed clock-info parse) with power management
  disabled, the very first watermark computation divides by zero.

## Trigger chain (unprivileged local user)

`drm_mode_setcrtc` (any DRM client with `/dev/dri/card0`, e.g. an X/wayland
user; no DRM-Master privilege needed beyond the fd) →
`dce_v11_0_crtc_do_set_base` → `dce_v11_0_bandwidth_update`
(dce_v11_0.c:2106) → `dce_v11_0_program_watermarks` (:1186) →
`dce_v11_0_latency_watermark` (:1085/:1124) → `div` by 0 → #DE → panic.
Also reached from `dce_v11_0_crtc_dpms` → `amdgpu_pm_compute_clocks`
bandwidth updates on DPMS transitions.

## What was run on the guest

`wm_div0.c` (argv-parameterized, so clang cannot fold the trap — the first
version did fold it into `ud2`/SIGILL, which is itself proof the divide is
reachable, but the shipped version traps the honest way):

- run.log, run.2.log: healthy-clock control returns 2297; zero-clock input
  → `Floating point exception (core dumped)`, exit 136 (signal 8 = SIGFPE =
  #DE), confirmed by `dmesg`: `pid ... (wm_div0) ... exited on signal 8`.
- env.txt: guest PCI bus = i440FX + bochs VGA (1234:1111) + virtio; no AMD
  display controller; no `/dev/dri`; no drm/amdgpu kld → the kernel path
  cannot attach on this guest.

## Classification per honest-status table

Guest reachable + target device absent → `not_reproduced`, `reproduced=0`,
`impact=none` (nothing kernel-level observed). Realistic impact ceiling on
equipped hardware: **local DoS via kernel panic** (Medium). Confidence:
certain (source trace + on-guest arithmetic proof of the fault class).

## Negative results (classes hunted and killed in this file)

- IV-ring OOB in `dce_v11_0_crtc_irq` (:3332-3333, no bounds check on
  `crtc = entry->src_id - 1`) and `dce_v11_0_pageflip_irq`
  (:3239-3242, array load before the bounds check): **killed** —
  `amdgpu_irq_dispatch` (amdgpu_irq.c:414-432) only invokes handlers for
  registered `(client_id, src_id)` pairs and validates
  `src_id < AMDGPU_MAX_IRQ_SRC_ID`; crtc_irq is registered only for
  src_id ∈ {1..num_crtc} (:2870) and pageflip_irq for src_id ∈ {8..18}
  (:2875), so the handler-side indices stay within `crtcs[6]`
  (AMDGPU_MAX_CRTCS=6, amdgpu_mode.h:65). Hardening nit only.
- Audio pin arrays: `audio.pin[AMDGPU_MAX_AFMT_BLOCKS=9]`
  (amdgpu_mode.h:261) vs `num_pins` ≤ 8 and `pin_offsets[8]` (:1426-1436)
  — in-bounds; afmt `dig->dig_encoder` ≤ 6 < `afmt[9]` — in-bounds;
  `dce_v11_0_audio_enable` NULL-checks the pin (:1419).
- `dce_v11_0_is_display_hung` `crtc_status[6]` vs `num_crtc` ≤ 6 —
  in-bounds; `vga_control_regs[6]` indexed by crtc_id ≤ 5 — in-bounds.
- num_heads divides at :967/:987 — protected by caller guard :1050.
- Suspend/resume: `dce_v11_0_suspend/resume` (:3003-3032) are plain
  hw_fini/hw_init wrappers with saved backlight; no lifetime issue found.

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