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

NULL deref / heap OOB read: cac_leakage_table indexed by unrelated SCLK count without bounds

Summary

smu7_setup_dpm_tables_v0 at :673 std_voltage_table=cac_leakage_table (may be NULL per processpptables.c:1470). Loop :716-721 indexes std_voltage_table->entries[i] bounded by allowed_vdd_sclk_table->count, NOT by std_voltage_table->count. NULL deref panic if cac_leakage_table absent. OOB read if shorter than sclk table. Fix: NULL check + bound by min(all table counts).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1167 Β· 11 files
FileTypeDescriptionSize
harness.c trigger-source userspace reconstruction of smu7_hwmgr.c:716-721; demonstrates NULL-deref + OOB read 5.3 KB view raw
build.sh build-script cc -O2 -Wall -o harness harness.c 205 B view raw
run.sh run-script ./harness 125 B view raw
build.log build-log successful in-guest build 86 B view raw
run.log run-log harness output: SIGSEGV case + OOB read case 1.1 KB view raw
env.txt environment uname, cc version, modstat for amdgpu 394 B view raw
fix.diff suggested-fix NULL check + bounds check in vddc DPM loop 1.2 KB view raw
VERDICT.md verdict full narrative 3.6 KB ↓ raw
README.md readme human-facing summary 2.9 KB ↓ 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-facing summary
↓ download raw

DF-1167 β€” smu7_setup_dpm_tables_v0 NULL-deref + OOB read

Finding

smu7_setup_dpm_tables_v0() at sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:666 reads std_voltage_table = hwmgr->dyn_state.cac_leakage_table (line 673-674) and then, in the loop at lines 716-721, indexes std_voltage_table->entries[i] bounded by allowed_vdd_sclk_table->count β€” not by std_voltage_table->count and without a NULL check.

cac_leakage_table is set to NULL at sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:1470 and only assigned a value when ptable5->usCACLeakageTableOffset != 0 (line 1472). A PowerPlay table that omits the CAC leakage table therefore leaves the pointer NULL, and the next call into smu7_setup_dpm_tables_v0 dereferences NULL at line 718 β†’ kernel panic.

If the table is present but shorter than vddc_dependency_on_sclk, the loop also performs an out-of-bounds read past the allocated cac_leakage_table.

Affected code

sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:673-674, 716-721

Reproducibility on this audit guest

The amdgpu / powerplay driver is optional (optional amdgpu drm in sys/conf/files); it is not compiled into X86_64_GENERIC and the QEMU guest has no AMD GPU. The bug therefore cannot be triggered live on the audit guest. The reproducibility proof is:

  1. Source-level trace confirming the data flow NULL β†’ deref (processpptables.c:1470 NULL assignment, no NULL check at smu7_hwmgr.c:718).
  2. Userspace harness (harness.c) that reconstructs the same struct layout and the same loop, demonstrates the NULL-deref crash (SIGSEGV at offset 0), and the OOB read when the table is shorter than the sclk table.

How to run the harness

./build.sh
./run.sh            # demonstrates both the NULL-deref and the OOB-read cases

Expected output (Linux/DragonFly userspace):

[case 1] std_voltage_table=NULL, sclk_count=3  -> reproducing kernel loop...
  BUG: dereferencing std_voltage_table->entries[0].Leakage with std_voltage_table=NULL
  harness: SIGSEGV (signal 11) at address (nil)   [kernel: NULL-deref panic]
[case 2] std_voltage_table->count=1, sclk_count=8 -> OOB read of entries[1..7]
  BUG: kernel would read 7 entries past cac_leakage_table->entries[0]

Threat model

The PowerPlay table is parsed from the GPU VBIOS / ACPI powerplay table at driver attach time. An attacker who can supply a crafted VBIOS (e.g. a re-flashed GPU, a malicious PCI device, or a hypervisor/emulator presenting a forged AMD GPU) can trigger an immediate kernel panic at boot or driver load.

On DragonFlyBSD the amdgpu driver is not in GENERIC, so this is a Medium severity, defense-in-depth issue for the default kernel, but a High DoS for any system that loads amdgpu (Radeon Southern Islands + Vega/Arctic Islands APU setups).

Add a NULL check and bound the loop by the smaller of the two table counts. See fix.diff.

VERDICT.md verdict full narrative
↓ download raw

DF-1167 β€” VERDICT

Verdict: REPRODUCED (source+harness). Not uid=0-escalatable β€” this is a NULL-deref / OOB-read DoS in optional GPU firmware-parsing code.

Bug confirmation

smu7_setup_dpm_tables_v0() at sys/dev/drm/amd/powerplay/hwmgr/smu7_hwmgr.c:666 reads std_voltage_table = hwmgr->dyn_state.cac_leakage_table at lines 673-674 with no NULL check, then in the loop at lines 716-721 indexes std_voltage_table->entries[i].Leakage bounded by allowed_vdd_sclk_table->count β€” not by std_voltage_table->count.

The pointer is provably NULL on a common path: sys/dev/drm/amd/powerplay/hwmgr/processpptables.c:1470 unconditionally sets hwmgr->dyn_state.cac_leakage_table = NULL; and only assigns a real table at line 1476 when ptable5->usCACLeakageTableOffset != 0 (line 1472). A PowerPlay table that omits the CAC leakage table therefore leaves the pointer NULL, and the next call into smu7_setup_dpm_tables_v0 dereferences NULL at line 718 (std_voltage_table->entries[i].Leakage) β†’ kernel panic.

Even when the table is present, if its count is smaller than vddc_dependency_on_sclk->count, the same loop performs an out-of-bounds read past the kzalloc'd cac_leakage_table (allocated at processpptables.c:1382 with size derived from the table's own ucNumEntries).

The mandatory-table assertions at lines 677-685 cover allowed_vdd_sclk_table and allowed_vdd_mclk_table, but no assertion guards std_voltage_table, confirming the reviewer's claim.

Reproducibility on the audit guest

  • amdgpu/radeon/powerplay are optional (sys/conf/files:2492+); they are not compiled into X86_64_GENERIC (the running #0 kernel) and the QEMU guest has no AMD GPU.
  • amdgpu.ko exists in /boot/modules/ but cannot initialize the powerplay path without real AMD GPU hardware, so a live kernel trigger is not possible on this guest.

A userspace harness (harness.c) reconstructs the exact struct layouts from hwmgr.h and replays the loop at smu7_hwmgr.c:716-721. Run on the guest (./build.sh && ./run.sh) it demonstrates both primitive classes:

[case 1] std_voltage_table=NULL, sclk_count=3 -> reproducing kernel loop...
  BUG: dereferenced std_voltage_table (NULL) at i=0
  harness: caught signal 11 (SIGSEGV/SIGBUS) -- kernel equivalent: NULL-deref panic

[case 2] std_voltage_table->count=1, sclk_count=8 -> OOB read
  i=1..7: BUG OOB read std_voltage_table->entries[i].Leakage (past allocated count=1)

Because this is a read primitive (NULL deref / OOB read), there is no memory-corruption write to convert into uid=0. The realistic impact ceiling is a kernel panic / DoS at driver attach time, plus a small info leak (OOB kernel heap read into the param1 field of the DPM table, which is later exposed through the PowerPlay sysfs/debugfs interface on a real GPU system).

Exploit chain

none β€” not a write primitive. DoS ceiling documented above.

Fix

fix.diff adds the missing NULL check (std_voltage_table != NULL) and the missing bounds check (i < std_voltage_table->count) to the loop, defaulting param1 to 0 when the std voltage entry is absent. The diff git apply --checks cleanly against the audit tree.

Threat model

The PowerPlay table is parsed from the GPU VBIOS / ACPI powerplay table at driver attach time. An attacker who can supply a crafted VBIOS (re-flashed GPU, malicious PCI device, or a hypervisor/emulator presenting a forged AMD GPU) triggers an immediate kernel panic at boot or module load. Severity Medium for the default GENERIC kernel (module not loaded), High for any system that actually loads amdgpu.

Fix verification

not_testable

compile+harness validated

module/object build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source+harness. smu7_setup_dpm_tables NULL cac_leakage_table deref. amdgpu not in GENERIC.