vega12_pp_tables_uninitialize NULL-deref on second pptable_fini: hwmgr_hw_init err1/err2 already freed hwmgr->pptable while pm_en/dpm_enabled stay set, so the next hwmgr_hw_fini (second pp_table sysfs write via amd_powerplay_reset, driver teardown, GPU reset) faults at NULL+16 β kernel panic
Summary
The fini derefs the cached pptable unguarded while hw_init's error paths already fini'd it once (pm_en stays stale-true, dpm_enabled never cleared on error) - a root pp_table write whose re-init fails arms the next fini to fault at NULL+16. Harness reproduced (SIGSEGV at the kernel-equivalent offset); NULL-guard fix compiled into a full -Werror amdgpu.ko. Secondary kzalloc leak on the err: path documented. Root-gated panic, DF-4546 convention.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4951 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| vega12_dfini_null.c | β | 16.4 KB | view raw | |
| build.sh | β | 124 B | view raw | |
| run.sh | β | 100 B | view raw | |
| run.log | β | 2.1 KB | view raw | |
| run.stability.log | β | 20 B | view raw | |
| fix_run.log | β | 2.8 KB | view raw | |
| fix.diff | β | 1005 B | view raw | |
| fix_build.log | β | 1.2 MB | β download | |
| env.txt | β | 691 B | view raw | |
| README.md | β | 3.1 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| manifest.json | β | 1.9 KB | view raw | |
| verdict.json | β | 5.3 KB | view raw |
DF-4951 β vega12_processpptables: double pptable_fini NULL deref
File: sys/dev/drm/amd/powerplay/hwmgr/vega12_processpptables.c
Class: CWE-476 (NULL pointer dereference) β kernel panic, root/hw-failure precondition
Severity: Low Β· Bucket: base:drm
What
vega12_pp_tables_uninitialize() (vega12_processpptables.c:296-320) caches
pp_table_info = hwmgr->pptable and immediately dereferences it
(kfree(pp_table_info->power_saving_clock_max), :301) with no NULL
guard. The caller contract makes hwmgr->pptable == NULL a reachable
state: hwmgr_hw_init()'s error paths err1:/err2: (hwmgr.c:257-262)
already invoke pptable_fini() once β freeing the pptable and NULLing
hwmgr->pptable β before returning an error, while hwmgr->pm_en stays
TRUE (it is only cleared when pptable funcs are missing, hwmgr.c:216-219)
and pm.dpm_enabled retains its stale boot-time TRUE (nothing clears it on
the error path).
Any subsequent hwmgr_hw_fini() then re-enters the function and faults at
NULL+offset. Reachable triggers after one failed powerplay re-init:
- a second root write to
/sys/class/drm/card0/device/pp_table(amdgpu_pm.c:463 β amd_powerplay.c:676pp_dpm_set_pp_table, gated only onhwmgr && hwmgr->pm_enβamd_powerplay_resetβhwmgr_hw_fini); - driver teardown / GPU reset paths that reach
pptable_fini(the pp IP block'sstatus.hwwas set at boot and is not cleared byamd_powerplay_reset).
The failing re-init itself is any hwmgr_hw_init failure at err1/err2
(backend_init / psm_init_power_state_table / phm_setup_asic /
enable_dsm failure β e.g. SMU task timeout after a pp_table swap, the
classic sleep/wake or table-upload hiccup).
Reproduce (lifted-copy harness; in-kernel path needs a Vega12 ASIC)
./build.sh # cc -O2 -Wall -o vega12_dfini_null vega12_dfini_null.c ./run.sh # ./vega12_dfini_null
Expected (run.log):
- CASE 1 BOOT-OK (healthy boot + healthy reset): clean exit.
- CASE 2 BUG (write#1 β pptable_init OK, backend FAILS β err1 fini inside
init; write#2 β fini again): SIGSEGV fault address 0x10
(power_saving_clock_max at phm_ppt_v3_information+16 β kernel-x86-64
offset) = the kernel panic-equivalent.
- CASE 3 CONTROL (write#1 fails at the header check β err: path leaks the
fresh kzalloc but does NOT fini; write#2 frees it): survives β proving the
crash specifically requires the err1/err2 (fini-inside-init) failure and
demonstrating the secondary kmem-leak defect on the err: path.
Patched build: cc -O2 -Wall -DPATCHED ... β CASE 2/4 survive (fix_run.log).
Fix validation
git apply --check fix.diffOK (host tree and guest/usr/src).- Full
amdgpu.komodule rebuild with the fix under the real kernel toolchain, -Werror,-j8: RC=0 in 24 s,vega12_processpptables.orecompiled at fix_build.log:346-347, 0 diagnostics (fix_build.log). - Guest
/usr/srcrestored and md5-verified afterwards.
Artifacts
vega12_dfini_null.c Β· build.sh Β· run.sh Β· build (in run.log header) Β· run.log Β· run.stability.log Β· fix_run.log Β· fix.diff Β· fix_build.log Β· env.txt Β· VERDICT.md Β· manifest.json Β· verdict.json
VERDICT β DF-4951
Reproduced: YES (lifted-copy harness on the audit guest; in-kernel path hardware-gated β no Vega12 ASIC)
Root cause (proven, path:line)
- vega12_processpptables.c:296-301 β
vega12_pp_tables_uninitialize()takespp_table_info = hwmgr->pptableand dereferences it at the very first statement (kfree(pp_table_info->power_saving_clock_max)) with no NULL guard. - hwmgr.c:222-231 + 257-262 β
hwmgr_hw_init()callspptable_init(:222) thenbackend_init(:229); on backend/power-state/ asic/DSM failure it jumps toerr1:/err2:which already callpptable_fini()before returning the error. After such a failurehwmgr->pptable == NULL. - hwmgr.c:216-219 β
pm_enis not cleared on these error paths (only when pptable/hwmgr funcs are missing); amdgpu_pm.c / amd_powerplay.c never clearpm.dpm_enabledon the error path either. - amd_powerplay.c:676-707 β
pp_dpm_set_pp_table(sysfspp_tablewrite, root-only) gates only onhwmgr && hwmgr->pm_en(:680-681) and callsamd_powerplay_reset(:697) βhwmgr_hw_fini(:665) βpptable_fini(hwmgr.c:280-281) β NULL deref atphm_ppt_v3_information+16(hwmgr.h:579-594 layout, x86-64). - The pp IP block's
status.hw(amdgpu_device.c:1572) was set at boot and is not cleared byamd_powerplay_reset, so driver teardown after a failed reset re-enters the same path.
Harness result (run.log)
- CASE 1 BOOT-OK: healthy init + healthy pp_table reset β clean.
- CASE 2 BUG: boot OK β root pp_table write#1 whose re-init fails at
backend_init (err1 β fini inside init,
hwmgr->pptable = NULL) β root pp_table write#2 (or teardown) βhwmgr_hw_finiβvega12_pp_tables_uninitializeβ SIGSEGV at fault address 0x10 (power_saving_clock_max= pptable+16) β the exact kernel-equivalent fault (kfree(NULL->power_saving_clock_max)). - CASE 3 CONTROL: write#1 failing at the header check takes the
err:path (hwmgr.c:223-224) which does NOT fini β write#2 then frees the leaked (non-NULL) pptable cleanly. This isolates the crash to the err1/err2 failure mode and simultaneously evidences the secondary defect: hwmgr->pptable (kzalloc'd at vega12:272) leaks on every pptable_init failure (theerr:path never finis); a root loop of pp_table writes with a header-invalid table grows kmem unboundedly (~small alloc per iteration). Not separately verified as a finding β noted here and folded conceptually into the fix discussion. - PATCHED (fix.diff NULL guard,
-DPATCHED): CASE 2/4 survive, no fault (fix_run.log).
Impact
Kernel NULL-deref panic (CWE-476), fixed fault offset (+16 read then kfree of garbage-free NULL β no data control). Precondition: one failed powerplay re-init (SMU/backend hiccup after a pp_table upload or during runtime re-init) plus a second root-triggered fini (pp_table write, rmmod/teardown, GPU reset). Root-or-failing-hardware gated β Low, consistent with the DF-4546 convention for pre-conditioned panics. No unprivileged path: the sysfs node is 0644 root-writable, and the failing re-init itself requires either root's table or an SMU failure.
Fix validation
git apply --checkOK on the read-only host tree and on guest/usr/src.- Full
amdgpu.korebuild with fix.diff applied, real kernel toolchain, -Werror,-j8: RC=0, 0 diagnostics, 24 s (fix_build.log; vega12_processpptables.o at :346-347). - Harness PATCHED control: the previously-fatal CASE 2 sequence survives.
- Guest
/usr/srcrestored (git apply -R), md5-verified against the pristine tree (085571ce1e8f9c2fafdd71e4e3b2cc95), module objdir cleaned.
Recommended fix
fix.diff: early-return NULL guard in vega12_pp_tables_uninitialize
(11 added lines, no behavior change for the healthy path). Longer term,
hwmgr.c's err: path should also fini (or init's early returns should
free) to close the leak β out of this file's scope, noted for the
maintainer.
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.
Fix verification
not_testablePatched kernel not runnable on the guest (no Vega12 ASIC). fix.diff is git-apply-able (host + guest), compiles clean in a full amdgpu.ko module build under the real kernel toolchain with -Werror (fix_build.log, RC=0, 0 diagnostics), and the harness PATCHED control survives the previously-fatal sequence.
fix.diff; fix_build.log (full module rebuild RC=0, vega12_processpptables.o recompiled at log:346-347, 0 'error:' lines); fix_run.log (PATCHED cases 2/4 survive, PATCHED_RUN_RC=0); guest /usr/src restored + md5-verified (085571ce1e8f9c2fafdd71e4e3b2cc95)
Confirmed kernel references
- sys/dev/drm/amd/powerplay/hwmgr/vega12_processpptables.c:296
- sys/dev/drm/amd/powerplay/hwmgr/vega12_processpptables.c:301
- sys/dev/drm/amd/powerplay/hwmgr/vega12_processpptables.c:272
- sys/dev/drm/amd/powerplay/hwmgr/vega12_processpptables.c:316
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:222
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:229
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:257
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:267
- sys/dev/drm/amd/powerplay/hwmgr/hwmgr.c:280
- sys/dev/drm/amd/powerplay/amd_powerplay.c:660
- sys/dev/drm/amd/powerplay/amd_powerplay.c:676
- sys/dev/drm/amd/powerplay/amd_powerplay.c:697
- sys/dev/drm/amd/amdgpu/amdgpu_pm.c:463
- sys/dev/drm/amd/amdgpu/amdgpu_pm.c:1992
- sys/dev/drm/amd/powerplay/inc/hwmgr.h:579
Detail
Exploit chain
root pp_table sysfs write #1 (or runtime powerplay re-init) fails at backend_init/err2 -> pptable_fini already ran, hwmgr->pptable=NULL, pm_en/dpm_enabled stale true -> root pp_table write #2 / rmmod / GPU reset -> hwmgr_hw_fini -> vega12_pp_tables_uninitialize -> kfree(NULL->power_saving_clock_max) load at NULL+16 -> kernel panic. DoS only: fixed NULL offset, no data control; requires one failed re-init plus a root-triggered (or hardware-failure) second fini.
Evidence (decisive lines)
run.log (CASE 2: '>>> SIGSEGV: fault address 0x10 (power_saving_clock_max = pptable + 0x10)' + 'PANIC-CLASS FAULT REPRODUCED'; CASE 1 clean; CASE 3 survives showing err:-path leak); run.stability.log (repeat runs); fix_run.log (PATCHED: case 2 + case 4 survive, PATCHED_RUN_RC=0); fix_build.log (full amdgpu.ko rebuild with fix.diff, -Werror, RC=0, vega12_processpptables.o at :346); fix.diff (git apply --check OK)
PoC changes
authored fresh this run (pass 2); no seed PoC existed
Verified recommended fix
NULL-guard early return in vega12_pp_tables_uninitialize (fix.diff); separately, hwmgr_hw_init's err: path should fini the partially initialized pptable to close the leak
Verdict
vega12_pp_tables_uninitialize (vega12_processpptables.c:296-301) dereferences hwmgr->pptable with no NULL guard at its first statement. hwmgr_hw_init's err1/err2 error paths (hwmgr.c:257-262) already invoke pptable_fini once, leaving hwmgr->pptable NULL, while hwmgr->pm_en stays TRUE (hwmgr.c:216-219 only clears it for missing funcs) and pm.dpm_enabled is never cleared on error; a subsequent hwmgr_hw_fini -- second pp_table sysfs write (amdgpu_pm.c:463 -> amd_powerplay.c:676/:697 amd_powerplay_reset, gated only on pm_en), driver teardown or GPU reset (pp IP block status.hw not cleared by the reset path) -- re-enters the function and faults at phm_ppt_v3_information+16 (hwmgr.h:579-594). Reproduced on the audit guest with a faithful lifted-copy harness: CASE 2 (pp_table write whose re-init fails at backend_init -> fini-inside-init, then a second write) SIGSEGVs at fault address 0x10, the exact kernel-equivalent NULL deref; CASE 1 (healthy) and CASE 3 (header-check failure on the err: path, which leaks but never finis) survive, isolating the crash to the err1/err2 mode and evidencing the secondary kmem-leak (vega12:272 kzalloc never freed on init failure); PATCHED build with the fix.diff NULL guard survives. In-kernel path hardware-gated (no Vega12 ASIC on the QEMU guest).
No comments yet.