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

Integer divide-by-zero in spread-spectrum programming when VBIOS SS rate == 0 (or truncates to 0 via /=100)

Summary

ci_calculate_sclk_params and ci_calculate_mclk_params divide by ss.rate copied verbatim from the VBIOS SS table: v1 usSpreadRateInKhz has no zero check and V2/V3 do ss->rate /= 100, truncating any value <100 (in 10Hz) to exactly 0. Executes for every sclk/mclk level when the SS caps are set (true whenever the SS table header parses). Kernel #DE -> panic at ci_dpm_enable and resume. Twin of si_dpm DF-3612; distinct sink from known DF-1143 (fan table deltas). PoC demonstrates the truncation and traps SIGFPE at the exact expression on the guest.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3621 Β· 8 files
FileTypeDescriptionSize
ci_ss_div0.c β€” 3.1 KB view raw
build.sh β€” 92 B view raw
run.sh β€” 116 B view raw
build.log β€” 393 B view raw
run.log β€” 393 B view raw
env.txt β€” 547 B view raw
VERDICT.md β€” 2.3 KB ↓ raw
fix.diff β€” 2.2 KB view raw
VERDICT.md
↓ download raw

DF-3621 VERDICT β€” divide-by-zero on VBIOS spread-spectrum rate == 0

Bottom line

Real bug, source-certain; runtime inconclusive (no AMD GPU on guest).

Two unguarded kernel integer divisions by the VBIOS-controlled spread spectrum rate:

  • ci_calculate_sclk_params() β€” ci_dpm.c:3337 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
  • ci_calculate_mclk_params() β€” ci_dpm.c:2981 clks = reference_clock * 5 / ss.rate;

ss.rate is filled by amdgpu_atombios_get_asic_ss_info() (amdgpu_atombios.c:896-992) with no zero validation in any of the three table revisions, and the V2/V3 paths additionally do ss->rate /= 100 (:959/:984), so even a non-zero VBIOS value below 100 (in 10Hz units) truncates to exactly 0.

Reachability: pi->caps_sclk_ss_support / caps_mclk_ss_support are set to true whenever the ASIC_InternalSS_Info header parses (ci_dpm.c:6052-6056); the divides then execute for every graphics/memory level during ci_dpm_enable β†’ ci_init_smc_table β†’ ci_populate_all_graphic_levels / ci_populate_all_memory_levels (ci_dpm.c:3432-3444 / 3479-3487) β€” i.e., at driver bind with a crafted VBIOS, and again on each resume.

On x86-64 a kernel integer division by zero raises #DE β†’ panic.

Verification narrative

  • Guest has no AMD ASIC (ramfb only) β†’ kernel path unexecutable β†’ inconclusive; module presence verified.
  • Logic PoC ci_ss_div0.c executed on the guest reproduces both the V2/V3 /= 100 truncation-to-zero and the actual machine division trapping (run.log: SIGFPE caught at sink [A], the ci_dpm.c:3337 expression; sinks [B]/[C] are the same instruction pattern).
  • Distinct from known DF-1143 (fan-table t_diff div-by-zero): different operands, different functions, different VBIOS fields. Twin: si_dpm DF-3612.

Fix

fix.diff wraps each SS-programming block in if (ss.rate) β€” skipping SS setup for a zero rate is safe (the SSEN enable bit is simply not set). Not testable on this guest (no AMD hardware).

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff skips SS programming when rate==0 (SSEN simply not enabled); not testable without AMD hardware.

fix.diff; no AMD ASIC on guest for kernel baseline/patched runs
↓ fix.diffper-fix-DF-3621

Confirmed kernel references

Detail

Exploit chain

crafted VBIOS with zero (or <100-in-10Hz) spread rate -> driver bind/resume -> integer #DE in kernel thread -> panic (DoS). No memory-corruption path.

Evidence (decisive lines)

run.log: 'usSpreadRateIn10Hz=50 -> ss.rate after /=100 = 0' followed by 'divide-by-zero trapped (SIGFPE ...) -> PANIC' at the ci_dpm.c:3337 expression; build.log clean.

PoC changes

authored from scratch; two iterations needed to defeat gcc -O2 const-folding of the divisors (made VBIOS values volatile, added SIGILL handler)

Verified recommended fix

Wrap the SS programming blocks in ci_calculate_sclk_params/ci_calculate_mclk_params in 'if (ss.rate)' (see fix.diff); optionally validate rate!=0 in amdgpu_atombios_get_asic_ss_info.

Verdict

Source-certain kernel divide-by-zero: ci_calculate_sclk_params (ci_dpm.c:3337) and ci_calculate_mclk_params (ci_dpm.c:2981) divide by ss.rate taken verbatim from the VBIOS SS table by amdgpu_atombios_get_asic_ss_info (amdgpu_atombios.c:936 v1 no zero check; :954+:959 / :976+:984 V2/V3 '/= 100' truncates any value <100 to exactly 0). Reached during ci_dpm_enable for every sclk/mclk level when the SS table header parses (caps_*_ss_support, ci_dpm.c:6052-6056). Kernel #DE -> panic. Runtime inconclusive: guest lacks any AMD ASIC; the truncation and the trapping division demonstrated on the guest by ci_ss_div0.c.