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)
PoC verification
Evidence pack
findings/poc/DF-1143 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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 inkernel.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.
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
- Driver is not in the kernel.
grep -E 'radeon|amdgpu' sys/config/X86_64_GENERICreturns nothing; both drivers ship as loadable modules only:$ ssh dfbsd 'ls /boot/kernel/ | grep -E "amdgpu|radeon" | head -3' amdgpu.ko radeon.konm /boot/kernel/kernel.debug | grep -ciE 'radeon|amdgpu'β 0. The running#0GENERIC kernel has zero radeon/amdgpu symbols compiled in, and no amdgpu module is loaded (kldstat | grep amdgpuis empty). - 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'sprobe()would never bind and the VBIOS parser atamdgpu_dpm.c:348would never execute. - 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=0chain 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.
Recommended fix
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_testablegit 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.
No comments yet.