β¬’ 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.9 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 65 B view raw
run.sh run-doc Documents why runtime trigger is impossible on this guest 89 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 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

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.