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)
PoC verification
Evidence pack
findings/poc/DF-1167 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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:
- Source-level trace confirming the data flow NULL β deref
(
processpptables.c:1470NULL assignment, no NULL check atsmu7_hwmgr.c:718). - 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).
Recommended fix
Add a NULL check and bound the loop by the smaller of the two table counts.
See fix.diff.
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/powerplayareoptional(sys/conf/files:2492+); they are not compiled intoX86_64_GENERIC(the running#0kernel) and the QEMU guest has no AMD GPU.amdgpu.koexists 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_testablecompile+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.
No comments yet.