β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1204

Divide-by-zero in spread spectrum clock calculation via ss.rate == 0

Summary

ni_calculate_sclk_params() at ni_dpm.c:2044: divides by ss.rate from VBIOS ASIC_InternalSS_Info V3 table. radeon_atombios_get_asic_ss_info divides ss.rate/=100 at radeon_atombios.c:1594; if BIOS usSpreadRateIn10Hz<100, ss.rate truncates to 0 -> divide-by-zero panic. Same at ni_populate_mclk_value() :2246. Sibling of DF-1128/DF-1143/DF-1166 div-by-zero fan pattern. Fix: check ss.rate==0 before division, return -EINVAL.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1204 Β· 9 files
FileTypeDescriptionSize
VERDICT.md verdict source-level analysis: ss.rate/=100 truncates to 0, downstream divides by zero 2.8 KB ↓ raw
fix.diff suggested-fix guard ss.rate<100 before /=100 in both v2 and v3 parsers 1.4 KB view raw
build.sh build-script no-op (no userspace PoC; driver-gated) 444 B view raw
run.sh run-script no-op (no AMD GPU on guest) 264 B view raw
fix_build.log build-log cumulative kernel build with all 5 fixes applied, NK_DONE rc=0 5.6 MB ↓ download
env.txt environment guest uname, PCI topology, target HW required 1.2 KB view raw
README.md readme human reproduce doc 976 B ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme human reproduce doc
↓ download raw

DF-1204 β€” radeon div-by-zero in spread-spectrum clock calc (build/run scripts)

Hardware+config-gated (AMD/ATI Cayman/Northern-Islands GPU + radeon.ko + ni_dpm). radeon is not in GENERIC and no AMD GPU is present on this guest. See VERDICT.md.

Files

  • VERDICT.md β€” detailed source-level analysis
  • fix.diff β€” git-apply-able patch guarding ss->rate /= 100
  • env.txt β€” guest environment snapshot
  • fix_build.log β€” kernel-build output (cumulative batch)

build.sh

#!/bin/sh
echo 'build.sh: no userspace PoC for DF-1204 (driver-gated).'
echo 'To validate the fix, apply fix.diff and build a kernel + modules:'
echo '  cd /usr/src && patch -p1 < fix.diff && make -j6 nativekernel KERNCONF=X86_64_GENERIC'

run.sh

#!/bin/sh
echo 'run.sh: DF-1204 is hardware-gated (AMD/ATI GPU + radeon.ko).'
echo 'radeon is not in GENERIC and no AMD GPU is present on this guest.'
echo 'See VERDICT.md for the source-level confirmation.'
VERDICT.md verdict source-level analysis: ss.rate/=100 truncates to 0, downstream divides by zero
↓ download raw

DF-1204 β€” radeon divide-by-zero in spread-spectrum clock calculation

Verdict

NOT REPRODUCED (source-confirmed; hardware+config-gated). The div-by-zero is real in both call sites that consume ss.rate, and the truncation that zeroes it is real in both the v2 and v3 parsers of the VBIOS ASIC_InternalSS_Info table. But radeon is not in GENERIC and no AMD/ATI GPU is present on this QEMU/KVM guest. No runtime trigger; validated by line-level source trace + single-fix kernel build.

Mechanism

radeon_atombios_get_asic_ss_info (sys/dev/drm/radeon/radeon_atombios.c:1506) parses the VBIOS ASIC_InternalSS_Info table. For the engine-clock (SCLK) and memory-clock (MCLK) spread-spectrum assignments the rate field is read in "10 Hz" units and then divided by 100 to convert to Hz:

  • v2 (crev==2), radeon_atombios.c:1564-1569: c ss->rate = le16_to_cpu(ss_assign->v2.usSpreadRateIn10Hz); ... if ((crev == 2) && ((id == ASIC_INTERNAL_ENGINE_SS) || (id == ASIC_INTERNAL_MEMORY_SS))) ss->rate /= 100;
  • v3, radeon_atombios.c:1586-1594: c ss->rate = le16_to_cpu(ss_assign->v3.usSpreadRateIn10Hz); ... if ((id == ASIC_INTERNAL_ENGINE_SS) || (id == ASIC_INTERNAL_MEMORY_SS)) ss->rate /= 100;

If the BIOS reports usSpreadRateIn10Hz < 100 (i.e. < 10 kHz), the integer divide truncates to 0. Downstream, that zero flows into:

  • ni_calculate_sclk_params (sys/dev/drm/radeon/ni_dpm.c:2044): c u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
  • ni_populate_mclk_value (ni_dpm.c:2246): c clk_s = reference_clock * 5 / (decoded_ref * ss.rate);

Both perform an integer division by ss.rate and would trap #DE (kernel panic) on a Cayman/Northern Islands GPU whose VBIOS carries a malformed spread-spectrum entry. This is the same family of div-by-zero panics as DF-1128/DF-1143/DF-1166 (fan div-by-zero).

Why not triggered on this guest

Same as DF-1200: radeon is not in GENERIC, no AMD GPU is present (vgapci0 is QEMU std VGA), and ni_dpm (Cayman/Northern-Islands DPM) only attaches to a small subset of radeon GPUs that are not emulated by QEMU. Option (d).

Defend the division at the source: in both the v2 and v3 branches of radeon_atombios_get_asic_ss_info, return false (no valid SS info) when ss->rate < 100 before the /= 100 truncation. This is preferable to guarding ni_calculate_sclk_params / ni_populate_mclk_value because it kills the bad value at the parser boundary, the same way a real BIOS malfunction should be reported, and the DPM callers already test the boolean return and disable SS when it is false.

Build validation

Cumulative kernel build with all 5 fixes applied β€” NK_DONE rc=0. See fix_build.log.

Reproduce

./build.sh    # no-op
./run.sh      # no-op

Fix verification

not_testable

compile validated

nativekernel rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. radeon ss.rate/=100 truncates to 0 -> div-by-zero in ni_dpm. radeon not in GENERIC.