goto restart_search reads OOB past power-state array returning OOB pointer used to program SMC
Summary
psm_set_user_performance_state (pp_psm.c:203-232): line 214 *state=hwmgr->ps resets cursor BEFORE restart_search: label at line 216. When user requests Battery/Balanced and no entry matches, switch at 223 sets label_id=Performance goto restart_search at 227. Goto targets 216 AFTER the 214 reset so *state is NOT re-reset. At goto point *state already points one element past kcalloc(table_entries,size) array (loop 217-221 advanced past last slot). Second pass iterates table_entries more at OOB offsets (table_entries+i)*ps_size reading (*state)->classification.ui_label. Performance==5 (0b101) bitwise AND matches almost any 4-byte OOB value -> return 0 with *state deep OOB. OOB pointer propagates as new_ps to psm_adjust_power_state_dynamic -> power_state_management -> requested->hardware dereferenced and forwarded to phm_set_power_state -> GPU SMC clock/voltage programming. Trigger: echo battery > /sys/class/drm/cardN/device/power_dpm_state on ASIC with no Battery/Balanced state (common desktop dGPUs). Impact: OOB heap read tens of KB past allocation leaking adjacent slab (KASLR); OOB pointer used as multi-KB hardware struct consumed by SMC -> panic/GPU hang/misprogrammed clocks. AV:L/PR:H/AC/L, C:H/A:H. Fix: move *state=hwmgr->ps to AFTER restart_search: label.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2078 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able fix | 335 B | view raw |
| VERDICT.md | verdict | source-trace confirmation | 717 B | β raw |
DF-2078 β psm_set_user_performance_state OOB read on restart_search
Verdict
REPRODUCED (source-only confirmation). Bug confirmed by source tracing.
Mechanism
psm_set_user_performance_state() (pp_psm.c:203-232) sets state=hwmgr->ps at line 214 BEFORE restart_search: label at 216. When Battery/Balanced not found, switch at 223-227 changes label to Performance and goto restart_search. But state is NOT reset (cursor advanced past table end), so the restarted loop reads out of bounds.
Fix
Move *state=hwmgr->ps to after the restart_search: label so the cursor resets on each search pass.
Batch-build status
Applied with all 24 other fixes; kernel + modules compiled rc=0, 0 errors, -Werror.
Fix verification
fixedMoved *state after restart_search label; batch build rc=0.
Moved *state after restart_search label; batch build rc=0.
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
psm_set_user_performance_state *state cursor not reset after restart_search.
Verified recommended fix
psm_set_user_performance_state *state cursor not reset after restart_search.
Verdict
psm_set_user_performance_state *state cursor not reset after restart_search.
No comments yet.