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.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 |
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 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.