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

Integer divide-by-zero in spread-spectrum programming when VBIOS usSpreadRateInKhz == 0 (si_calculate_sclk_params:4831 and si_populate_mclk_value:4931) β€” distinct sink from DF-1137

Summary

radeon_atombios_get_asic_ss_info copies ss->rate from the VBIOS ASIC_InternalSS_Info entry verbatim (no zero check) and returns true on match. si_calculate_sclk_params:4831 divides by (reference_divider * ss.rate) and si_populate_mclk_value:4931 divides by ss.rate for every power-level conversion whenever pi->sclk_ss/mclk_ss is armed by the same VBIOS tables. rate==0 -> div -> #DE -> kernel panic during si_dpm_enable state upload. VERIFIED: harness executes the exact expressions in forked children with rate=0 - both die SIGFPE (the userland face of kernel #DE); fixed-path rate=0 -> rc=-1 no trap, rate=30 -> rc=0 normal. Same malicious-VBIOS DoS family as DF-1137 (fan t_diff) but independent sinks.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3612 Β· 12 files
FileTypeDescriptionSize
harness.c β€” 5.0 KB view raw
build.sh β€” 66 B view raw
run.sh β€” 90 B view raw
build.log β€” 11 B view raw
run.log β€” 821 B view raw
fix.diff β€” 1.1 KB view raw
fix_build.log β€” 1.6 KB view raw
fix_run.log β€” 196 B view raw
VERDICT.md β€” 2.8 KB ↓ raw
README.md β€” 1.6 KB ↓ raw
manifest.json β€” 755 B view raw
verdict.json β€” 2.9 KB view raw

DF-3612 β€” Integer divide-by-zero in spread-spectrum programming when VBIOS usSpreadRateInKhz == 0 (si_dpm.c:4831 and :4931)

Severity: Medium Confidence: certain Status: reproduced (harness) Impact: panic

What

radeon_atombios_get_asic_ss_info() copies ss->rate from the VBIOS ASIC_InternalSS_Info entry verbatim and returns true; si_calculate_sclk_params:4831 divides by (reference_divider * ss.rate) and si_populate_mclk_value:4931 divides by ss.rate whenever the SS entry matches the target clock (pi->sclk_ss/pi->mclk_ss set from the same VBIOS table). rate==0 -> div -> #DE -> kernel panic during si_dpm_enable power-state upload. Harness executes the exact expressions in forked children: SIGFPE (signal 4) on both sinks == kernel #DE panic. No AMD GPU on the audit guest for a live trigger.

Reproduce

sh build.sh   # cc -O2 -Wall -o harness harness.c
sh run.sh     # prints [A] buggy-path behaviour, [B] fixed-path behaviour

Expected decisive output (see run.log): the [A] section shows the fault or corruption the kernel would take; the [B] section shows the behaviour with fix.diff applied.

No AMD GPU is attached to the audit QEMU guest (virtio-only), so the radeon DPM path cannot be exercised live; the harness replicates the exact kernel expressions with crafted-VBIOS inputs (house methodology, cf. DF-1137..1140).

Fix

fix.diff β€” validated: git apply --check clean; make si_dpm.o in sys/dev/drm/radeon with KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC compiles -Werror clean with all five pass-2 fixes stacked; harness [B] shows the primitive gone.

VERDICT.md
↓ download raw

DF-3612 β€” Verdict

Verdict: REPRODUCED (source-level + harness) β€” integer divide-by-zero (#DE) panic from crafted-VBIOS SS rate

Same malicious-VBIOS DoS family as DF-1137 (fan t_diff) but a distinct sink pair not covered by it.

Bug confirmation

The SS rate comes straight from the VBIOS with no zero check:

radeon_atombios.c:1543-1548   ss->percentage = le16(usSpreadSpectrumPercentage);
                              ss->type       = ucSpreadSpectrumMode;
                              ss->rate       = le16(usSpreadRateInKhz);   // no check
                              return true;                                // "found"

si_dpm.c then divides by it on every matching state:

si_dpm.c:4829-4832  (si_calculate_sclk_params, pi->sclk_ss)
    if (radeon_atombios_get_asic_ss_info(rdev, &ss, ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
        u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);   // :4831 #DE
        u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);          // :4832 #DE when clk_s==0

si_dpm.c:4929-4932  (si_populate_mclk_value, pi->mclk_ss)
    if (radeon_atombios_get_asic_ss_info(rdev, &ss, ASIC_INTERNAL_MEMORY_SS, freq_nom)) {
        u32 clks = reference_clock * 5 / ss.rate;                          // :4931 #DE

pi->sclk_ss / pi->mclk_ss are enabled by rv770_get_engine_memory_ss (si_dpm.c:7029) from the SAME VBIOS SS tables, so a crafted BIOS that sets the SS-support firmware flag plus a zero-rate entry arms both sinks. The functions run for every performance level of every power state during si_dpm_enable (si_convert_power_level_to_smc -> si_populate_sclk_value / si_populate_mclk_value). On x86-64, div with divisor 0 raises #DE -> kernel panic.

Harness confirmation

run.log:

[A1] engine SS ... child died from SIGFPE (signal 4): integer divide by zero
[A2] memory SS ... child died from SIGFPE (signal 4): integer divide by zero
[B]  rate=0 -> rc=-1 (SS skipped, no trap) ... rate=30 -> rc=0

Trigger conditions (not met on this guest)

SI GPU + radeon DPM (guest is virtio-only) + crafted VBIOS SS entry.

Exploit chain

None β€” #DE is not steerable; boot-time DoS panic.

Fix

fix.diff β€” if (ss.rate == 0) return -EINVAL; in both SS blocks. Validation: apply-check OK; make si_dpm.o -Werror clean (stacked); harness [B] no trap, sane-rate path unchanged (rc=0, clk values computed).

fix_status: fixed (compile + harness-level).

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

fixed
baseline reproduced→ patch + rebuild →patched clean

git apply --check OK (standalone and stacked with the other four si_dpm.c fixes); targeted module TU compile make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC si_dpm.o => rc=0, -Werror clean (87080 B). Harness fixed-path shows the primitive gone (fix.diff adds if (ss.rate == 0) return -EINVAL; in both SS blocks. git apply --check OK; stacked module TU compile -Werror clean; harness [B] no trap with sane-rate path still rc=0.)

fix.diff adds `if (ss.rate == 0) return -EINVAL;` in both SS blocks. git apply --check OK; stacked module TU compile -Werror clean; harness [B] no trap with sane-rate path still rc=0.
↓ fix.diffper-fix-DF-3612

Confirmed kernel references

Detail

Exploit chain

crafted VBIOS SS entry with usSpreadRateInKhz=0 + SS tables present -> sclk/mclk SS programming on every power-level conversion -> div by 0 -> #DE -> panic at DPM enable (boot-time DoS)

Evidence (decisive lines)

run.log [A1]/[A2]: 'child died from SIGFPE (signal 4): integer divide by zero' at both :4831 and :4931 replicas; [B]: 'rate=0 -> rc=-1 (SS skipped, no trap)'

PoC changes

n/a (pass-2 finding authored with its harness; no seeded PoC to fix)

Verified recommended fix

see fix.diff

Verdict

Same malicious-VBIOS DoS family as DF-1137 (fan-table t_diff) but a distinct sink pair. radeon_atombios_get_asic_ss_info() copies ss->rate from the VBIOS ASIC_InternalSS_Info entry verbatim and returns true; si_calculate_sclk_params:4831 divides by (reference_divider * ss.rate) and si_populate_mclk_value:4931 divides by ss.rate whenever the SS entry matches the target clock (pi->sclk_ss/pi->mclk_ss set from the same VBIOS table). rate==0 -> div -> #DE -> kernel panic during si_dpm_enable power-state upload. Harness executes the exact expressions in forked children: SIGFPE (signal 4) on both sinks == kernel #DE panic. No AMD GPU on the audit guest for a live trigger.