# DF-4583 — VBIOS zero clock values reach dfixed_div as divisors in the rs690 watermark math (kernel divide-by-zero)

Sibling pack of **DF-4582** (same harness, same function, different
attacker model).  The PoC sources, build and run scripts live in
`../DF-4582/`; this pack adds the scenario map and the fix for the two
VBIOS-derived zero-divisor sinks.

## The two sinks (rs690_crtc_bandwidth_compute, sys/dev/drm/radeon/rs690.c)

1. **sideport mclk == 0** — `rs690_pm_info()` (rs690.c:80/94) copies
   `ulBootUpMemoryClock` (crev 1) or `ulBootUpSidePortClock` (crev 2)
   verbatim from the VBIOS IntegratedSystemInfo table into
   `rdev->pm.igp_sideport_mclk`; neither field has a zero fallback (unlike
   `igp_system_mclk`, which has one at rs690.c:96-101).  When the VBIOS
   also reports sideport memory present (`radeon_atombios_sideport_present`,
   rs690.c:164), the compute path runs
   `b.full = dfixed_div(igp_sideport_mclk, 1000)` → 0, then
   `read_delay_latency = dfixed_div(read_delay_latency, b)` —
   **divide by zero** (rs690.c:371-372).
2. **default engine clock == 0** — `radeon_atombios_get_clock_info`
   (radeon_atombios.c:1240-1241) copies `ulDefaultEngineClock` into
   `rdev->clock.default_sclk` with **no zero fallback** (contrast
   usMaxPixelClock at :1269-1271 and vco_freq at :1285-1287 which do get
   fallbacks).  `radeon_update_bandwidth_info` then sets
   `rdev->pm.sclk = 0`, `core_bandwidth = pm.sclk/16 = 0`; every
   `max_bandwidth` replacement guard (`&& bandwidth.full`, rs690.c:366-380)
   keeps it at 0, and `sclk.full = dfixed_div(a, sclk)` at rs690.c:388
   **divides by zero**.

## Attacker model

Crafted or corrupt VBIOS (GPU option ROM reflash / card with attacker ROM /
broken BIOS image).  Effect: kernel panic at boot or at the first
modeset/bandwidth update.  Availability only — no memory unsafety.

## Evidence

* **Shipped binary** (`/boot/kernel/radeon.ko` disassembly):
  `0x5adb9: div %r8` where `r8 = (mclk << 16) >> 12` (i.e. mclk·16,
  built at 0x5adac-0x5adb6) — **r8 == 0 exactly when
  igp_sideport_mclk == 0** ⇒ #DE in the shipped code (sink 1 confirmed in
  the real binary).  The `1000/sclk` sink is the same structure
  (runtime `div %rsi` at 0x5ae6c following `shl $0xc` of max_bandwidth·16).
* **Guest userland, -O0** (harness scenario 1 and 2,
  `../DF-4582/rs690_div0.c`): both killed by **SIGFPE** (signal 8) —
  source semantics divide by zero (see DF-4582 run logs).
* **Guest kernel module, kernel flags**: scenario 1 survived in the
  harness build because gcc UB-transformed that particular chain in the
  harness context (documented in DF-4582/VERDICT.md); the shipped-binary
  disassembly above is the ground truth that the real radeon.ko keeps the
  runtime division.  Scenario 0 (DF-4582) panicked the guest with
  `Fatal trap 18`, proving the harness faithfully reproduces the
  dfixed_div trap when the division survives to runtime.
* **Fix validation**: `../DF-4582` WITH_FIX build (guards mirror this
  pack's fix.diff) survives scenarios 1 and 2 with
  `DF4582-FIX: zero divisor avoided at rdl-mclk / 1000/sclk — bailing with
  safe watermark`, module unloads cleanly.

## Reproduce (userland semantics; guest has no RS690 hardware)

    cd ../DF-4582
    cc -O0 -o rs690_div0 rs690_div0.c
    ./rs690_div0 1    # sideport mclk 0  -> SIGFPE
    ./rs690_div0 2    # pm.sclk 0        -> SIGFPE
