# DF-3878 VERDICT — divide-by-zero in dce_v8_0_latency_watermark (DCE8)

**Status: not_reproduced (kernel path) / arithmetic primitive reproduced 3/3 —
source-certain.** Same hardware gate as DF-3857 (DCE10) and DF-3828 (DCE11):
this guest has no AMD ASIC (pciconf vendor 0x1002 count = 0, no /dev/dri —
env.txt), so the kernel instruction cannot execute here. What is proven:

## Root cause (path:line into this tree)

* `dce_v8_0_latency_watermark()` divides by `available_bandwidth` at
  **dce_v8_0.c:817** (`(512*8*1000)/available_bandwidth`) and **:818**
  (`(128*4*1000)/available_bandwidth`), and by `wm->disp_clk` at **:819**
  (`40000000/wm->disp_clk`) — all BEFORE the function's only guard
  (`if (wm->num_heads == 0) return 0;`, **:827**).
* The num_heads guard is dead anyway: the sole caller chain
  `dce_v8_0_crtc_do_set_base` **:1973** → `dce_v8_0_bandwidth_update`
  **:1082-1100** → `dce_v8_0_program_watermarks` **:948** gates on
  `amdgpu_crtc->base.enabled && num_heads && mode` (**:959**).
* Zero divisor traced end-to-end: with `!adev->pm.dpm_enabled`
  (**:967**), `wm_high.yclk = adev->pm.current_mclk * 10` (**:973**) and
  `wm_high.sclk = adev->pm.current_sclk * 10` (**:974**).
  `pm.current_mclk/current_sclk` are seeded from VBIOS
  `ulDefaultMemoryClock/ulDefaultEngineClock` with NO zero-guard
  (amdgpu_atombios.c:678/680 — neighboring fields at :667/:694/:708 are
  clamped, these two are not) and are assigned EVEN ON PARSE FAILURE
  (amdgpu_atombios.c:718-719). One zero term collapses
  `dce_v8_0_available_bandwidth` (:760-768, = min of :642/:700/:729 terms)
  to 0 → #DE → kernel panic. Identical seeding to DF-3828/3857; the
  atombios parser is asic-agnostic.
* The `wm->disp_clk == 0` vector (mode->clock) is KILLED in this tree:
  `drm_mode_convert_umode` → `drm_mode_validate_driver`
  (drm_modes.c:1745) → `drm_mode_validate_basic` (drm_modes.c:1114)
  rejects clock==0 with MODE_CLOCK_LOW on the SETCRTC path; probed modes
  pass the same check in drm_probe_helper.c:368-375.
* Second entry: `amdgpu_pm_compute_clocks` → `amdgpu_display_bandwidth_update`
  (amdgpu_pm.c:2141) → same function on every DPM clock recompute.

## Reproduction

* `wm_dce8_div0.c` — exact replica of the fixed20_12-truncated bandwidth
  math and the three divides. Affected inputs (zero clocks): SIGFPE,
  exit=136, **3/3 runs**, dmesg `pid NNNN (wm_dce8), uid 1001: exited on
  signal 8` (run.log). Healthy-clock control (1200000/500000 kHz) returns
  2297 without trapping.
* In-kernel effect: the identical `div` instruction raises #DE in kernel
  mode → panic during an unprivileged DRM mode set on any Bonaire/Kaveri/
  Kabini/Mullins in the zero-clock state → **local DoS**. No memory-safety
  impact (pure div0).

## Pass-2 negatives checked and killed (dce_v10/v11 template)

* hpd arrays: `AMDGPU_HPD_NONE = 0xff` (amdgpu_mode.h:92) so every
  `hpd >= num_hpd` guard (:224, :248, :276, :318, :2942, :3133) rejects it;
  hpd_offsets[6] vs num_hpd=6 OK.
* audio: pin[AMDGPU_MAX_AFMT_BLOCKS=9] (amdgpu_mode.h:261) vs num_pins<=7
  (:1374-1383) OK; afmt[9] vs num_dig<=7 (KAVERI, cik.c:2023) OK;
  dig_offsets[7] covers it (:71-79).
* crtc: crtc_offsets[6] vs num_crtc<=6 (:385-405); vblank_get_counter
  (:144-150) lacks a `crtc < 0` check but every caller passes validated
  unsigned pipe (amdgpu_kms.c:1129-1156 via amdgpu_get_vblank_counter_kms
  which guards `pipe >= num_crtc`, amdgpu_kms.c:1109-1113);
  get_scanoutpos (:198-208) checks both bounds itself.
* ISR indices: `dce_v8_0_crtc_irq` (:3013-3018) indexes
  interrupt_status_offsets[6] with src_id-1 — bounded by the registration
  contract (sw_init :2645-2649 registers only 1..num_crtc; dispatch looks
  up client.sources[src_id], amdgpu_irq.c:426-431). Same for hpd_irq
  (:3126-3139, entry->src_data[0] checked at :3133 BEFORE use).
* pageflip_irq `crtcs[crtc_id]` read-before-check (:3082-3085): latent
  ordering defect (Linux moved the check first upstream), but crtc_id is
  derived from src_id∈{8..18} (registered :2651-2655) → in [0,5]; not
  attacker-reachable — hardening note only, consistent with the dce10
  pass-2 treatment.
* dfixed_const(wm->yclk) u32 overflow for large clocks (drm_fixed.h:35)
  degrades watermark math on ALL dce versions — correctness, not memory
  safety; shared drm_fixed.h, out of this file's scope.
* LUT loop (:2030-2035) reads 256 entries; gamma_size forced 256
  (:2584 drm_mode_crtc_set_gamma_size(&base,256)) → exact.
* cursor_set2 (:2248-2331): GEM handle via drm_gem_object_lookup with
  file_priv; width/height upper-bounded (:2268-2272); width==0 causes only
  a harmless (u32)-1 register field. No priv issue.

## Fix

Guard the divides (mirror of the num_heads guard) and zero-guard the VBIOS
default-clock seeding — see fix.diff (git-apply-clean against sys/).

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