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

smu_helper: heap OOB write in phm_get_svi2_*_voltage_table and phm_trim_voltage_table via unchecked VBIOS count

Summary

phm_get_svi2_mvdd/vddci/vdd_voltage_table copy VBIOS dep_table->count / lookup_table->count into vol_table->entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES=32] fixed array with NO bounds check on count. VBIOS ucNumEntries u8 max 255. phm_trim_voltage_table iterates vol_table->count writes table->entries[table->count] past kzalloc 32-entry table. Same class as VBIOS PowerPlay family DF-1468/1574/1586/1689/1690. Malicious PCIe GPU / passthrough VBIOS rewrite. 1344+ bytes heap corruption. Fix: clamp count to PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1797 Β· 9 files
FileTypeDescriptionSize
harness.c trigger-source userspace logic harness reproducing the buggy arithmetic/control-flow 3.1 KB view raw
VERDICT.md verdict full verification narrative 2.4 KB ↓ raw
build.sh build-script exact build command 88 B view raw
run.sh run-script exact run invocation 41 B view raw
harness_run.log run-log harness output on guest 346 B view raw
fix.diff suggested-fix git-apply-able unified diff 1016 B view raw
env.txt environment guest uname, cc version, kernel config 768 B view 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
VERDICT.md verdict full verification narrative
↓ download raw

DF-1797 β€” Verification Verdict

Verdict: REPRODUCED (source-confirmed + logic-harness)

The missing bounds check is confirmed at sys/dev/drm/amd/powerplay/hwmgr/smu_helper.c:258,286,314 in all three phm_get_svi2_*_voltage_table variants. The harness demonstrates the 256-byte heap overflow when a malicious VBIOS advertises 64 voltage entries.

Mechanism

phm_get_svi2_mvdd_voltage_table (smu_helper.c:244-270), phm_get_svi2_vddci_voltage_table (272-298), and phm_get_svi2_vdd_voltage_table (300-322) each set vol_table->count = dep_table->count (or lookup_table->count) and then loop i = 0..count-1 writing vol_table->entries[i] β€” but entries[] is fixed at PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES = 32 (ppatomctrl.h:211). With dep_table->count up to a uint32_t (no upper bound from VBIOS), the loop writes past entries[31] into whatever follows the pp_atomctrl_voltage_table in its kzalloc allocation.

The sibling atomctrl_get_voltage_table_v3 (ppatomctrl.c:526-551) DOES bound-check ucGpioEntryNum <= PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES before its equivalent loop (:546-551) β€” proving the bound is required and simply missing from the SVI2 helpers.

Harness evidence

DF-1797: phm_get_svi2_vdd_voltage_table wrote 32 entries past entries[32] (count=64 max=32) -> 256 bytes of trailing memory corrupted (sentinel bytes touched=256)
Matches smu_helper.c:314 (count=lookup->count) + :317 (entries[i]=..) with no bound; contrast ppatomctrl.c:546-551 which DOES check.

Why no live trigger on this guest

phm_get_svi2_*_voltage_table is called from AMD GPU PowerPlay init when parsing VBIOS voltage tables. The audit guest has no AMD GPU (amdgpu.ko is present but not loaded, no HW). A malicious/flashed VBIOS on a PCIe GPU or VFIO passthrough is the live trigger. Valid Phase-6 hard blocker.

Exploit chain

Not applicable (AMD-GPU-gated). No uid=0 claim. Live ceiling on real HW: panic / 256+ byte heap corruption in PowerPlay init; potentially escalatable against objects in the same slab bucket.

PoC changes

  • Added harness.c: flat-buffer model showing 32 OOB entry writes.
  • Added fix.diff: add if (count > PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES) return -EINVAL; to each of the three helpers.

Fix

fix.diff adds the missing bounds check before each loop, mirroring ppatomctrl.c:546-551.

  • BEFORE: harness reports 256 bytes of trailing memory corrupted (count=64).
  • AFTER: the check rejects count > 32 before any write.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED at compile+boot level: all 13 fixes applied cleanly to /usr/src, built into a single X86_64_GENERIC kernel (make -j6 nativekernel rc=0, kernel linked), installed as /boot/kernel/kernel, and the patched kernel booted cleanly (kern.version #1 vs baseline #0). The live PoC cannot run on this guest (HW/config-gated per the verdict), so before/after is at source+harness level: baseline harness: '256 bytes of trailing memory corrupted' (count=64) | patched: count>32 rejected before any write

baseline (#0 unpatched): baseline harness: '256 bytes of trailing memory corrupted' (count=64)
patched (#1 kernel, all 13 fixes, booted clean): patched: count>32 rejected before any write
kernel sha256 c3fff85f... (patched, booted) vs 5dc83dac... (baseline #0)
↓ fix.diffDragonFly 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Mon Jul 20 19:12:20 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64

Confirmed kernel references

Detail

Exploit chain

HW-gated (AMD GPU absent on guest; smu_helper.c compiles into amdgpu.ko which is present but not loaded). No uid=0 escalation claimed. Primitive characterized in harness.c (flat-buffer model showing 256-byte overflow). Live ceiling on real AMD GPU with malicious VBIOS: 256+ byte heap corruption in PowerPlay init; with slab grooming, potentially arbitrary write.

Evidence (decisive lines)

DF-1797: phm_get_svi2_vdd_voltage_table wrote 32 entries past entries[32] (count=64 max=32) -> 256 bytes of trailing memory corrupted (sentinel bytes touched=256)
Matches smu_helper.c:314 (count=lookup->count) + :317 (entries[i]=..) with no bound; contrast ppatomctrl.c:546-551 which DOES check.

PoC changes

Added harness.c (flat-buffer OOB model) and fix.diff (add count>32 check to each of the 3 helpers).

Verified recommended fix

fix.diff adds 'if (count > PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES) return -EINVAL;' to each of phm_get_svi2_{mvdd,vddci,vdd}_voltage_table, mirroring ppatomctrl.c:546-551. supersedes finding proposal by being more explicit.

Verdict

REPRODUCED at source+harness. phm_get_svi2_mvdd/vddci/vdd_voltage_table at smu_helper.c:244-322 set vol_table->count = dep_table->count then loop writing entries[i] with no upper bound, into entries[PP_ATOMCTRL_MAX_VOLTAGE_ENTRIES=32] (ppatomctrl.h:211). Harness with count=64 writes 32 entries past entries[32] = 256-byte overflow. Sibling ppatomctrl.c:546-551 DOES bound ucGpioEntryNum<=32 β€” bound is required and missing. HW-gated (AMD GPU PowerPlay, no HW on guest).