VBIOS-derived zero clocks reach dfixed_div as divisors in the rs690 watermark math: igp_sideport_mclk == 0 (no zero fallback) and ulDefaultEngineClock == 0 (pm.sclk == 0) β integer-divide-fault panic at boot/first bandwidth update
Summary
ulBootUpSidePortClock is copied with no zero fallback (unlike igp_system_mclk) - a VBIOS reporting sideport present with mclk 0 makes rs690.c:372 divide by zero; independently radeon_atombios stores default_sclk with no fallback, so pm.sclk==0 makes every guard's max_bandwidth 0 and :388 divides 1000 by sclk==0. Crafted/corrupt option ROM panics at boot/first modeset - trusted-VBIOS model, availability only. Harness scenarios SIGFPE + shipped-binary disassembly prove both sinks; WITH_FIX build survives. Treat-zero-as-absent + safe-watermark fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4583 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.3 KB | β raw | |
| fix.diff | β | 1.9 KB | view raw | |
| verdict.json | β | 4.3 KB | view raw |
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)
- sideport mclk == 0 β
rs690_pm_info()(rs690.c:80/94) copiesulBootUpMemoryClock(crev 1) orulBootUpSidePortClock(crev 2) verbatim from the VBIOS IntegratedSystemInfo table intordev->pm.igp_sideport_mclk; neither field has a zero fallback (unlikeigp_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 runsb.full = dfixed_div(igp_sideport_mclk, 1000)β 0, thenread_delay_latency = dfixed_div(read_delay_latency, b)β divide by zero (rs690.c:371-372). - default engine clock == 0 β
radeon_atombios_get_clock_info(radeon_atombios.c:1240-1241) copiesulDefaultEngineClockintordev->clock.default_sclkwith no zero fallback (contrast usMaxPixelClock at :1269-1271 and vco_freq at :1285-1287 which do get fallbacks).radeon_update_bandwidth_infothen setsrdev->pm.sclk = 0,core_bandwidth = pm.sclk/16 = 0; everymax_bandwidthreplacement guard (&& bandwidth.full, rs690.c:366-380) keeps it at 0, andsclk.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.kodisassembly):0x5adb9: div %r8wherer8 = (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). The1000/sclksink is the same structure (runtimediv %rsiat 0x5ae6c followingshl $0xcof 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-4582WITH_FIX build (guards mirror this pack's fix.diff) survives scenarios 1 and 2 withDF4582-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
Fix verification
inconclusiveGuard-level validation only: the WITH_FIX harness build demonstrably avoids both zero divisors ('DF4582-FIX: zero divisor avoided at rdl-mclk / 1000/sclk') and survives, and the guards are value-based (independent of how the zero arises). The unpatched KLD harness did not panic on scenario 1 in its particular -O2 codegen (gcc UB-transformed that chain there β see DF-4582 VERDICT), while the shipped radeon.ko disassembly (0x5adb9) proves the real binary keeps the runtime division, so a hardware baseline was not executable and fix_status is honestly inconclusive rather than fixed.
fix.diff (zero sideport mclk treated as no-sideport with UMA latency + zero max_bandwidth bail); ../DF-4582/fix_run.log.
Confirmed kernel references
Detail
Exploit chain
crafted/corrupt GPU VBIOS (option-ROM reflash or malicious card) -> rs690_pm_info parses IntegratedSystemInfo with sideport-present flag and zero boot clocks (rs690.c:80/94/164) or radeon_atombios_get_clock_info stores default_sclk=0 (radeon_atombios.c:1240-1241, no zero fallback unlike :1269-1271) -> first bandwidth update (boot modeset / pm profile change) -> rs690_crtc_bandwidth_compute divides by the zero fixed-point value (rs690.c:371-372 or :386-388) -> do_div by zero -> integer divide fault -> kernel panic.
Evidence (decisive lines)
../DF-4582/run.2.log (SIGFPE x2 for scenarios 1 and 2 at -O0); ../DF-4582/objdump_radeon_ko.txt (0x5adb9 div %r8, divisor r8=(mclk<<16)>>12 == 0 iff mclk==0, in the shipped binary); ../DF-4582/fix_run.log ('zero divisor avoided at rdl-mclk / 1000/sclk' with survival and clean unload in the guarded build); fix.diff in this pack.
PoC changes
No seed; shares the DF-4582 harness (scenarios 1 and 2 map to the two sinks).
Verified recommended fix
rs690.c: treat igp_sideport_mclk==0 as 'no sideport' (UMA read-delay latency) and bail with the safe watermark when max_bandwidth (pm.sclk-derived) is zero; optionally add zero fallbacks where radeon_atombios_get_clock_info stores default_sclk/default_mclk.
Verdict
Two VBIOS-derived zero-divisor sinks in rs690_crtc_bandwidth_compute() panic the kernel via dfixed_div: (1) igp_sideport_mclk==0 (copied verbatim from the VBIOS IntegratedSystemInfo ulBootUpSidePortClock/ulBootUpMemoryClock by rs690_pm_info, no zero fallback) with sideport reported present divides by zero at rs690.c:371-372; (2) ulDefaultEngineClock==0 gives rdev->pm.sclk==0 and a zero max_bandwidth that every '&& bandwidth.full' guard preserves, dividing by zero at rs690.c:386-388. Evidence: -O0 verbatim harness kills both scenarios with SIGFPE (run.2.log); the SHIPPED /boot/kernel/radeon.ko contains the runtime division for sink 1 (0x5adac-0x5adb9: r8=(mclk<<16)>>12, div %r8 β zero exactly when mclk==0) proving the real binary does not fold it away; the WITH_FIX harness build guards both sinks and survives. End-to-end execution needs RS690-class hardware with the crafted VBIOS (absent in the QEMU guest), and the KLD harness scenario-1 happened to be UB-transformed by gcc in that build context (documented), so overall status is inconclusive/likely rather than reproduced.
No comments yet.