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

32-bit arithmetic overflow in SMU7 fan-RPM math: 60 * crystal_clock_freq * 10000 wraps for 25/27 MHz VI reference clocks, skewing reported fan speed (hwmon fan1_input/fan1_target) and the programmed TACH target by a constant ~7.09x (DF-4761 vega10 twin)

Summary

Both RPM directions evaluate the constant product in u32 - for SMU7's 25/27 MHz xclk the product is 1.5e10 and wraps before the division: reported RPM wrong and the TACH TARGET_PERIOD short by exactly 7.09x for every request. Telemetry/hw-reliability only. Guest demo of the exact expressions; kernel path hw-gated. u64-cast fix (same as DF-4761).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4892 Β· 6 files
FileTypeDescriptionSize
rpm_wrap_demo.c β€” 2.2 KB view raw
build.sh β€” 210 B view raw
run.sh β€” 131 B view raw
run.log β€” 1.7 KB view raw
VERDICT.md β€” 2.5 KB ↓ raw
verdict.json β€” 2.6 KB view raw
VERDICT.md
↓ download raw

DF-4892 VERDICT β€” arithmetic defect certain; kernel path untested (no SMU7 hardware on guest)

Bottom line

smu7_thermal.c:100 and :273 compute 60 * crystal_clock_freq * 10000 in u32 before dividing. For SMU7 parts amdgpu_asic_get_xclk() returns spll.reference_freq = 25000/27000 kHz (vi.c:326-349), so the product is 1.5e10/1.62e10 and wraps to 2115098112/3315098112 β€” a factor-of-~7.09 error that propagates BOTH to the RPM reported via hwmon fan1_input/fan1_target (amdgpu_pm.c:1204/:1266) and to the TACH TARGET_PERIOD programmed by fan1_target writes (amdgpu_pm.c:1297, root-only). Identical shape and severity class to DF-4761 (vega10_thermal, Info).

Why untested

Verification of the kernel path requires Tonga/Fiji/Polaris/CZ hardware (fan controller registers + tach); the QEMU guest has no AMD GPU, so the PHM_READ_VFPF_INDIRECT_FIELD reads cannot execute. Per contract (Low/Info findings) PoC verification is skipped. The arithmetic itself is demonstrated by rpm_wrap_demo (run.log, executed on the guest userspace), which evaluates the exact source expressions in u32 vs u64: wrap YES for xclk=25000/27000, reported-RPM error up to 1.29e10x, TARGET_PERIOD off by a constant 7.09x for every RPM request.

Verified-by-inspection details

  • 8 * speed at :273 cannot overflow (speed <= fanInfo.ulMaxRPM from the atom pptable, u32 but small; speed==0 and min/max range checked at :260-266).
  • smu7_thermal_get_temperature() (:286-302): 10-bit field, 0x200 sentinel, 511*1000 fits int β€” clean.
  • smu7_thermal_set_temperature_range() (:311-338): inputs clamped to [0,255] degC before the /1000 writes; DIG_THERM_INTH/INTL/DPM fields fit β€” clean.
  • percent paths (:54-81, :195-225): u64 do_div math, outputs clamped β€” clean.
  • hwmgr->fan_ctrl_enabled = true even when the SMC StartFanControl message fails (:171-177) and smu7_thermal_start_smc_fan_control ignores its return (:427-431) β€” thermal-correctness only, not security; noted, not filed.

Fix

u64-cast before the division at both sites (see README.md / finding fix block).

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix not built/validated: requires SMU7 hardware to exercise the fan-controller path; the one-line u64-cast fix is identical in shape to the DF-4761 fix and is arithmetically sufficient (demo's u64 column = correct values)

findings/poc/DF-4892/run.log u32 vs u64 columns
per-fix-DF-4892

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log: 'xclk=25000 u32: 2115098112 u64: 15000000000 wrap: YES' + TARGET_PERIOD 'off by 7.09x' rows; smu7_thermal.c:100/:273; vi.c:326-349; smu7_hwmgr.c:5086-5087; amdgpu_pm.c:1204/1266/1297

PoC changes

wrote rpm_wrap_demo.c evaluating the exact kernel expressions in u32 vs u64 (userspace arithmetic demo; kernel path hardware-gated)

Verified recommended fix

u64-cast the product before dividing at smu7_thermal.c:100 and :273: (uint64_t)60 * crystal_clock_freq * 10000 / ...

Verdict

DF-4761 twin confirmed by inspection: smu7_thermal.c:100 and :273 compute 60crystal_clock_freq10000 in u32; vi_get_xclk() returns 25/27 MHz-class reference_freq for SMU7 parts, so the product wraps (1.5e10 -> 2115098112) before the division, skewing both the hwmon-reported fan RPM and the programmed TACH TARGET_PERIOD by a constant ~7.09x. Info severity: telemetry/hw-reliability only (write path root-only), no memory safety. Kernel-path PoC not executed: the QEMU guest has no AMD GPU (no SMU7 fan controller), and Info findings are skipped per contract; arithmetic divergence demonstrated by the userspace demo (run.log) evaluating the exact source expressions in u32 vs u64.