Unchecked smu8 power-state casts: NULL deref (panic) when VBIOS powerplay table has no Boot-classified state β DF-4143 family twin for smu8
Summary
The cast_smu8_power_state helpers return NULL whenever the state's magic doesn't match, and smu8 dereferences the results unchecked at four sibling sites. psm_init_power_table only stamps hwmgr->request_ps/current_ps with the smu8 magic by memcpy'ing a Boot-classified state; nothing requires such a state to exist. With a crafted VBIOS lacking the Boot flag, init completes with current_ps magic-0, and the first runtime transition dereferences NULL at offset 0x30 -> panic. Also exposes the ps->level==0 -> levels[0xFFFFFFFF] underflow in get_performance_level. Crafted-VBIOS local DoS tied to firmware. NULL-guard x4 fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4169 Β· 21 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.2 KB | β raw | |
| VERDICT.md | β | 9.1 KB | β raw | |
| env.txt | β | 1.3 KB | view raw | |
| fix.diff | β | 1.9 KB | view raw | |
| build.log | β | 2.7 KB | view raw | |
| run.log | β | 10.1 KB | view raw | |
| run.2.log | β | 10.1 KB | view raw | |
| run.3.log | β | 10.1 KB | view raw | |
| verdict.json | β | 6.8 KB | view raw | |
| manifest.json | β | 2.4 KB | view raw | |
| rig/gen_rig.sh | β | 7.2 KB | view raw | |
| rig/check_verbatim.sh | β | 5.5 KB | view raw | |
| rig/rig_shims.h | β | 7.0 KB | view raw | |
| rig/smu8_rig.c | β | 23.7 KB | view raw | |
| rig/Makefile | β | 448 B | β download | |
| rig/build.sh | β | 509 B | view raw | |
| rig/run.sh | β | 1.2 KB | view raw | |
| rig/smu8_slice.inc | β | 9.3 KB | β download | |
| rig/smu8_slice_fixed.inc | β | 9.5 KB | β download | |
| rig/smu8_slice_verbatim.h | β | 36.7 KB | view raw | |
| rig/rig_fix.diff | β | 1.8 KB | view raw |
DF-4169 β smu8 power-state casts unchecked: NULL deref when VBIOS has no Boot-classified state
- File: sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c primary site: smu8_apply_state_adjust_rules :1041-1045 cast β :1051 NULL+0x30 WRITE, :1066 NULL+0x40 read sibling sites: smu8_dpm_get_sclk :1316-1321 (NULL+0x4/0x44; levels[0xFFFFFFFF] when level==0), smu8_get_current_shallow_sleep_clocks :1598-1601 (levels[0xFFFFFFFF]), smu8_get_performance_level :1569-1572 (clamp defeat β unclamped levels[index])
- Window creator: sys/dev/drm/amd/powerplay/hwmgr/pp_psm.c psm_init_power_state_table (kzalloc'd request_ps/current_ps only receive the smu8 magic via memcpy of a Boot-classified state; no Boot state in the VBIOS powerplay table => magic stays 0)
- Class: NULL pointer dereference + level==0 index underflow, CWE-476/CWE-125. Hardware-gated (Carrizo/Bristol Ridge SMU8 APU, crafted VBIOS powerplay table). DF-4143 family twin for smu8.
Verification (2026-10-15, verbatim-rig evidence class)
REPRODUCED in byte-identical production code on the DragonFly guest (no AMD GPU β the in-kernel path is ASIC-gated; see VERDICT.md Β§1):
cd rig && sh build.sh /usr/src # 26 verbatim blocks checker-proven, builds rig_base + rig_fixed sh run.sh # baseline + fixed (this pack's fix.diff) over 11 modes
Expected (see run.log):
- baseline
4169-*modes: SIGSEGV classified β WRITE at NULL+0x30 (smu8_apply_state_adjust_rules :1051), READ at NULL+0x4 / NULL+0x44 (smu8_dpm_get_sclk), ~64 GB wild reads (levels[0xFFFFFFFF], :1321/:1601), OOB read 324 B past the 320 B state allocation (smu8_get_performance_level clamp defeat, :1571-1572); - baseline
4169-boot-chain-control: no fault (Boot flag present β magic stamped) β isolates the precondition; - fixed build: all
4169-*modes return -EINVAL, no fault;clean-validoutput byte-identical to baseline;4171-*modes still fault under this fix (orthogonal finding DF-4171).
Logs: build.log, run.log, run.2.log, run.3.log (three byte-identical runs). Full narrative: VERDICT.md. fix.diff authored against the read-only sys/ tree, A/B validated in the rig, never applied to sys/.
DF-4169 VERDICT (verify run, 2026-10-15)
Status: REPRODUCED (verbatim-rig evidence class). Every defect the
finding enumerates was demonstrated in byte-identical production code β
the NULL WRITE at NULL+0x30 exactly as filed, two more NULL reads, the
literal levels[0xFFFFFFFF] wild reads (~64 GB) from level==0, and the
smu8_get_performance_level clamp-defeat OOB read β and the pack's
fix.diff was A/B-validated in the same rig: fixed (all guarded modes
return -EINVAL, clean-valid byte-identical). In-kernel execution is
AMD-ASIC-gated on this guest (no AMD GPU β proven, env.txt).
1. Method
The guest cannot attach amdgpu (0 vendors 0x1002, no /dev/dri). Following
the DF-4165/4247/4388/4396 evidence class, the actual production
functions were compiled into a guest-side harness:
rig/gen_rig.shextracts the cited smu8_hwmgr.c functions byte-identical: :49-97 (smu8_magic,cast_smu8_power_state,cast_const_smu8_power_state,smu8_get_eclk_level), :99-127, :129-158 (level helpers), :248-252 (smu8_convert_8Bit_index_to_voltage), :1037-1080smu8_apply_state_adjust_rules, :1251-1278, :1303-1322smu8_dpm_get_sclk, :1556-1593smu8_get_performance_level, :1595-1604smu8_get_current_shallow_sleep_clocks, :1846-1874 β plus 16 header slices (power_state.h 26-187, hardwaremanager.h caps/platform descriptor/ PP_Clocks/PHM_PerformanceLevel, hwmgr.h magic enum + dependency tables, smu8_hwmgr.h state structs, cz_ppsmc.h message ids, kgd_pp_interface.h forced-level enum).rig/check_verbatim.shre-verified all 26 blocks byte-identical β run on the guest against/usr/src/sys, the tree its running kernel was compiled from (build.log:ALL VERBATIM CHECKS PASSED; guest /usr/src smu8_hwmgr.c md5 a0cb59b723434a78e88bf0580d5a5508 == repo).- The fixed build is the same functions + exactly this pack's fix.diff
(applied to a copy of the pristine full file with
patch --fuzz=0; proven bidirectionally: forward apply no rejects, reverse apply restores the pristine file byte-exactly; the rig applies a header-normalized copy whose hunk bodies arecmp-identical to the pack's fix.diff β only the---/+++path lines differ, checker-proven). - Harness shims (
rig/rig_shims.h) touch no defect path: recording shims forsmum_send_msg_to_smc_with_parameter/smu8_nbdpm_pstate_enable_\ disable/smu8_enable_disable_uvd_dpm; minimizedstruct pp_hwmgr/phm_dynamic_state_info/smu8_hwmgr/amd_pp_display_configurationwith production names/types for every field the functions and the fix consult; power states allocated exactly as pp_psm.c does (kzalloc β ZEROED, size = sizeof(pp_power_state) + sizeof(smu8_power_state) β the production ps_size formula), VCE/UVD tables exactly as processpptables.c does. Allocations are guard-backed: end-anchored (first byte past the object faults) and lead-guard (first byte before the object faults); the fault classifier prints address, READ/WRITE (x86 #PF error bit frommc_err), and the class.
2. Observed (run.log; byte-identical in run.2.log/run.3.log, md5-equal)
Layout: need_dfs_bypass at offset 0x30 (48) β the finding's cited
offset, confirmed; action at 0x40; levels at 68; smu8_power_level=16B;
levels[0xFFFFFFFF] = state+0x1000000034 (64.0 GB).
Baseline β verbatim production code, per finding site:
| mode | observation | verbatim site |
|---|---|---|
| 4169-boot-chain | psm loop over a 2-state VBIOS with no Boot flag leaves request_ps/current_ps magic=0 (kzalloc'd, never memcpy'd); first smu8_dpm_get_sclk β SIGSEGV READ at NULL+0x4 (smu8_ps->level through the NULL cast) |
:1316β:1321; chain pp_psm.c:53-94 |
| 4169-boot-chain-control | same VBIOS WITH one Boot state β magic stamped, get_sclk(high)=60000, no fault β the precondition is exactly the Boot flag |
pp_psm.c:89-94 |
| 4169-null-adjust-rules | SIGSEGV WRITE at NULL+0x30 β smu8_ps->need_dfs_bypass = true through the NULL cast (exactly the filed "NULL at offset 0x30") |
:1041/:1044 β :1051 |
| 4169-null-get-sclk | SIGSEGV READ at NULL+0x44 β smu8_ps->levels[0].engineClock |
:1316 β :1319 |
| 4169-level0-get-sclk | magic-valid state with level==0 β read at state+68719476900 (~64 GB) = levels[level-1] = levels[0xFFFFFFFF] (uint32 wrap) |
:1321 |
| 4169-level0-perf-unclamped | level==0 defeats the :1571 clamp (9 > 0xFFFFFFFF false) β levels[9] read 324 B past the end of the 320 B state allocation (12 B past the smu8 tail) |
:1571-1572 |
| 4169-level0-shallow-sleep | read at state+68719476906 (~64 GB) β literal ps->levels[ps->level - 1] with level==0 |
:1601 |
| clean-valid | all five functions on a valid 2-level state: rc=0, values exactly as computed (get_sclk 30000/60000, vddc 1525, msgs [0x20,0x1]/[0x1c,0x1]) | β |
Fixed (this pack's fix.diff, identical inputs): every 4169 mode
returns -EINVAL (rc=-22; get_sclk returns it through its uint32_t return
as 4294967274) without faulting; boot-chain still returns no clock but
no NULL deref; clean-valid output byte-identical to baseline. The two
DF-4171 modes still fault under this fix (orthogonality, see DF-4171's
pack). Fix A/B validated.
3. Primitive and impact
- Kernel NULL-page WRITE (
:1051, NULL+0x30) and NULL reads (:1066NULL+0x40 viasmu8_current_ps->action,:1319NULL+0x44): kernel-mode fault on the unmapped null page β fatal trap β kernel panic. Impact class: panic (local DoS), exactly as filed. - Wild ~64 GB reads (
levels[0xFFFFFFFF], :1321/:1601): guaranteed unmapped in kernel space β fatal trap β panic. (Not an info leak: the value would have to be mapped and returned; the fault is the outcome.) - OOB read past the power-state allocation (:1571-1572 clamp defeat):
reads adjacent kernel heap (up to
indexunclamped) intolevel->coreClockβ a PHM_PerformanceLevel that sysfs/DPM queries return to userland; a bounded heap over-read with a userland-visible sink in-kernel (in the rig: guard-detected at +324/+320).
Threat model (family's established one): crafted/corrupted GPU VBIOS
(Carrizo/Bristol Ridge SMU8 β the smu8_hwmgr.c hwmgr binds to CZ APUs)
parsed at driver attach, or root re-parsing a crafted blob via
/sys/class/drm/card0/device/pp_table. No unprivileged userland trigger
on stock hardware β same class as DF-4143/4247. Demonstrated
out-of-kernel context (rig); the in-kernel consequence (NULL write β
panic) follows from the verbatim instruction stream.
4. Fix validation
rig_fixed = same verbatim slice + this pack's fix.diff (checker-proven
identity, bidirectional patch proof). All 4169 modes: no fault β
guards return -EINVAL for NULL casts and level==0; clean-valid:
byte-identical to baseline. The 4171 modes faulting under this build
prove the two findings' fixes are orthogonal and that the 4169 modes are
not artifacts. A rebuilt-kernel validation was not performed: the fix
sites execute in-kernel only on AMD ASICs absent from this guest; per the
verbatim-rig protocol the fixed slice was A/B-validated in the same rig.
fix.diff is authored against the pristine read-only sys/ tree and was
never applied there (guest /usr/src verified pristine by md5 before and
after: a0cb59b723434a78e88bf0580d5a5508).
5. Honest limitations
- Demonstrated with production instructions and production struct layouts in a userland mapping, not the kernel heap; the harness builds the precondition (zeroed/level-0 states) directly rather than by parsing a full crafted ATOM ROM (the psm loop that produces the zeroed request_ps/current_ps is harness-reimplemented from pp_psm.c:76-95 and driven with/without the Boot flag β the control run isolates the precondition exactly).
smu8_dpm_get_sclkreturns uint32_t, so the fixed build's -EINVAL is observable as 4294967274 (production return-type quirk; callers at this call site only log/format the value).- The finding's summary attributes the
levels[0xFFFFFFFF]underflow toget_performance_level; measured, :1571 in that function manifests as clamp defeat (unclampedlevels[index]OOB), while the literallevels[level-1]0xFFFFFFFF wrap occurs at :1321 and :1601. Both variants demonstrated; both covered by the fix'slevel == 0guards.
6. Bottom line
The finding is verified end to end: the four unchecked cast sites and the level==0 arithmetic reproduce in byte-identical production code at the exact filed offsets (NULL+0x30 WRITE, NULL+0x4/0x40/0x44 reads, ~64 GB wild reads, clamp-defeat OOB read), the precondition (no Boot-classified state β zeroed request_ps/current_ps) is isolated by a control run, and the pack's fix.diff neutralizes every demonstrated mode on identical input while leaving valid-state behavior byte-identical.
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
fixedA/B validated in the same rig: rig_fixed = same verbatim slice + this pack's fix.diff (forward apply with --fuzz=0, no rejects; reverse apply restores the pristine file byte-exactly; hunk bodies cmp-identical to the pack's fix.diff). On identical inputs every demonstrated 4169 mode is neutralized (rc=-EINVAL, no fault, no NULL write, no wild read, no OOB read) while clean-valid output stays byte-identical to baseline; the DF-4171 modes still fault under this fix, proving orthogonality (they are a different finding). Rebuilt-kernel validation not performed: the fix sites execute in-kernel only on AMD ASICs absent from this guest (verbatim-rig protocol, DF-4165/4247/4388/4396).
run.log FIXED sections: '4169-null-adjust-rules rc=-22 ... returned WITHOUT faulting (guard active in fixed build)'; '4169-level0-perf-unclamped rc=-22 coreClock=0'; clean-valid lines identical to baseline; check output in build.log: 'ALL VERBATIM CHECKS PASSED' incl. 'smu8_slice_fixed.inc == pristine file + fix.diff (function blocks)' and 'reverse-applying fix.diff restores the pristine file byte-exactly'
Confirmed kernel references
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:51
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:59
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1041
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1051
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1066
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1316
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1319
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1321
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1569
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1571
- sys/dev/drm/amd/powerplay/hwmgr/smu8_hwmgr.c:1601
- sys/dev/drm/amd/powerplay/hwmgr/pp_psm.c:53
- sys/dev/drm/amd/powerplay/hwmgr/pp_psm.c:90
Detail
Exploit chain
crafted/corrupted VBIOS (flashed ROM or vfio-pci romfile) on a Carrizo/Bristol Ridge SMU8 APU -> amdgpu attach -> pptable parse produces no Boot-classified state -> psm_init_power_table leaves request_ps/current_ps zeroed -> driver init completes -> first runtime power-state transition (smu8_apply_state_adjust_rules) or sysfs clock query (smu8_dpm_get_sclk) dereferences the NULL cast -> kernel-mode fault on the unmapped null page -> panic (local DoS). level==0 states additionally give ~64GB wild reads and clamp-defeat OOB reads past the state allocation. No unprivileged userland trigger on stock hardware; no content control beyond the fault.
Evidence (decisive lines)
findings/poc/DF-4169/run.log: '[BASELINE] MODE 4169-null-adjust-rules -> !! SIGSEGV (addr=0x30, WRITE) => NULL DEREF: WRITE at NULL+0x30'; 'MODE 4169-level0-get-sclk -> fault 68719476900 bytes from the state base (~64GB): WILD levels[0xFFFFFFFF]-STYLE READ'; 'MODE 4169-level0-perf-unclamped -> fault 324 bytes PAST THE END of the 320B object'; '[FIXED]' sections show rc=-22/no fault for every 4169 mode; build.log: 26 verbatim blocks ALL PASSED on the guest against /usr/src; run.2/run.3.log md5-identical
PoC changes
Seed pack had no runnable PoC ('not runnable here'). Built the verbatim rig per the DF-4388 pattern: gen_rig.sh/check_verbatim.sh extract and guest-verify 26 byte-identical blocks (10 function slices incl. all four cited cast sites + their verbatim callees, 16 header slices); rig_shims.h minimizes pp_hwmgr/dyn_state/smu8_hwmgr with production field names/types, recording shims for the three off-defect-path SMU helpers, guard-backed kzalloc shims (end-anchored + lead-guard); smu8_rig.c drives 11 modes with a read/write-classifying fault handler (DragonFly mc_err). Fixed build applies the pack's fix.diff to a pristine copy (header-normalized, hunk bodies cmp-identical) and re-extracts the patched functions; both directions patch-proven.
Verified recommended fix
NULL-guard the four unchecked cast sites and bail on level==0: apply fix.diff (return -EINVAL when smu8_ps/smu8_current_ps/ps is NULL or ps->level==0 in smu8_apply_state_adjust_rules, smu8_dpm_get_sclk, smu8_get_performance_level, smu8_get_current_shallow_sleep_clocks).
Verdict
Every defect the finding enumerates was demonstrated in byte-identical production smu8_hwmgr.c code on the DragonFly guest (verbatim-rig evidence class; in-kernel execution is AMD-ASIC-gated, no AMD GPU in the guest). The finding's exact NULL+0x30 write was reproduced: with a VBIOS power table lacking any Boot-classified state, psm_init_power_table (pp_psm.c:53-94) leaves hwmgr->request_ps/current_ps kzalloc-zeroed (magic=0), cast_smu8_power_state returns NULL, and smu8_apply_state_adjust_rules:1051 executes 'smu8_ps->need_dfs_bypass = true' as a kernel NULL-page WRITE at exactly offset 0x30 (fault classifier: SIGSEGV addr=0x30 WRITE) -- in-kernel a fatal trap/panic. Sibling sites confirmed the same way: NULL+0x4 and NULL+0x44 reads in smu8_dpm_get_sclk (:1321/:1319), the literal levels[0xFFFFFFFF] wild reads ~64GB out (:1321, :1601) when a magic-valid state has level==0, and the smu8_get_performance_level clamp defeat (:1571: 'index > 0xFFFFFFFF' is never true, so index passes unclamped) reading 324B past the 320B state allocation (:1572). A Boot-flag control run isolates the precondition exactly, and the pack's fix.diff was A/B-validated in the same rig: all guarded modes return -EINVAL without faulting, clean-valid output byte-identical, and the DF-4171 modes keep faulting under it (orthogonality proof). Three runs byte-identical.
No comments yet.