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

Divide-by-zero in ci_thermal_setup_fan_table from VBIOS-controlled fan temperature deltas

Summary

ci_thermal_setup_fan_table at ci_dpm.c:1122-1129: t_diff1=fan.t_med-fan.t_min and t_diff2=fan.t_high-fan.t_med from VBIOS u16 used as divisors with no validation. If t_med==t_min or t_high==t_med: integer divide-by-zero -> kernel #DE panic during DPM enable. Reached at every boot/resume. Attacker: malicious VBIOS reflash/VFIO/QEMU. Fix: validate deltas non-zero and monotonic.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1143 Β· 8 files
FileTypeDescriptionSize
VERDICT.md verdict Full source trace + reachability analysis 3.6 KB ↓ raw
fix.diff suggested-fix Guard zero deltas before divide (git apply-able, validated) 544 B view raw
build.sh build-doc Documents why no executable PoC is possible 652 B view raw
run.sh run-doc Documents why runtime trigger is impossible on this guest 410 B view raw
env.txt environment uname + kernel symbol inventory proving amdgpu absent 407 B view raw
README.md readme human reproduce doc 693 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-1143 β€” divide-by-zero in amdgpu ci_thermal_setup_fan_table

Result

NOT REPRODUCED on this guest. Real source-level defense-in-depth bug; fix authored. See VERDICT.md for the full analysis.

Build & run

./build.sh    # documents why no executable PoC is possible
./run.sh      # documents why runtime trigger is impossible

Reachability summary

  • amdgpu is not in X86_64_GENERIC (0 symbols in kernel.debug)
  • No AMD GPU on the QEMU guest
  • Attacker model is malicious VBIOS, not unprivileged local

Fix

fix.diff β€” guard zero t_diff1/t_diff2 before the divide at sys/dev/drm/amd/amdgpu/ci_dpm.c:1128-1129. Applies cleanly with git apply.

VERDICT.md verdict Full source trace + reachability analysis
↓ download raw

DF-1143 β€” divide-by-zero in ci_thermal_setup_fan_table (amdgpu)

Verdict

NOT REPRODUCED on this guest (defense-in-depth source bug confirmed; path unreachable on the default GENERIC DragonFlyBSD kernel running in QEMU). Real source-level defect; fix.diff authored.

Mechanism (source trace, bug confirmed real)

In sys/dev/drm/amd/amdgpu/ci_dpm.c:1094, ci_thermal_setup_fan_table() computes the slope of the GPU fan PWM curve from three VBIOS-supplied u16 temperatures:

/* sys/dev/drm/amd/amdgpu/ci_dpm.c:1122-1129 */
t_diff1 = adev->pm.dpm.fan.t_med - adev->pm.dpm.fan.t_min;   /* L1122 */
t_diff2 = adev->pm.dpm.fan.t_high - adev->pm.dpm.fan.t_med;  /* L1123 */
...
slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100); /* L1128 */
slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100); /* L1129 */

t_diff1 and t_diff2 are integer divisors with no zero-check. Both temperatures are populated directly from the AtomBIOS PowerPlay table in sys/dev/drm/amd/amdgpu/amdgpu_dpm.c:360-362:

adev->pm.dpm.fan.t_min  = le16_to_cpu(fan_info->fan.usTMin);
adev->pm.dpm.fan.t_med  = le16_to_cpu(fan_info->fan.usTMed);
adev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh);

If a (potentially malicious) VBIOS image encodes usTMin == usTMed or usTMed == usTHigh, the kernel executes an integer divide-by-zero at L1128 or L1129 β†’ x86 #DE β†’ kernel panic during DPM enable (every boot/resume on the affected card).

Why it does NOT reproduce on this guest

  1. Driver is not in the kernel. grep -E 'radeon|amdgpu' sys/config/X86_64_GENERIC returns nothing; both drivers ship as loadable modules only: $ ssh dfbsd 'ls /boot/kernel/ | grep -E "amdgpu|radeon" | head -3' amdgpu.ko radeon.ko nm /boot/kernel/kernel.debug | grep -ciE 'radeon|amdgpu' β‡’ 0. The running #0 GENERIC kernel has zero radeon/amdgpu symbols compiled in, and no amdgpu module is loaded (kldstat | grep amdgpu is empty).
  2. No AMD GPU hardware in the QEMU guest. The guest is a serial-console VM (vtnet0/vtblk0, no GPU passthrough); there is no AMD Radeon device on the virtual PCI bus, so even with the module loaded the driver's probe() would never bind and the VBIOS parser at amdgpu_dpm.c:348 would never execute.
  3. Threat model is malicious-VBIOS, not unprivileged local. Even on real AMD hardware, the trigger is a malformed AtomBIOS image β€” the attacker model is "reflashed VBIOS / VFIO PCIe passthrough of a hostile GPU," not an unprivileged local user issuing syscalls. No uid=0 chain exists from this primitive on a default kernel.

Exploit chain

none β€” the primitive (kernel #DE / DoS) is gated behind AMD hardware + module load + attacker-controlled VBIOS, none of which exist on this guest.

PoC changes

none. No PoC source is provided because the path is unreachable on this kernel/guest combination (no driver in kernel, no hardware, attacker class is malicious-VBIOS not unprivileged-local). Reproducibility would require either (a) a physical AMD Bonaire/Hawaii/Kabini GPU with a crafted VBIOS image, or (b) VFIO PCIe passthrough of such a GPU into a VM with the amdgpu module loaded.

Author a guard before the divisions: if either delta is zero (or negative in u16 wrap), disable ucode fan control and return early, mirroring the existing duty100 == 0 early-return at L1113. See fix.diff β€” applies cleanly with git apply (validated). This matches the pattern of the upstream Linux hardening and the finding markdown's proposal.

Fix verification

not_testable

git apply --check validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ci_thermal_setup_fan_table div-by-zero. amdgpu not in GENERIC, no AMD GPU.