NULL boot_ps page-fault panic in sumo_dpm_enable (:1235) / sumo_dpm_disable (:1280) when the VBIOS power table has no BOOT-classified state or dpm.init failed β sumo twin of DF-4118/4143/4124/3972/3754/3953
Summary
sumo_parse_pplib_non_clock_info sets boot_ps only for BOOT-classified states and the parse still returns 0, so a BOOT-less crafted VBIOS parses 'successfully' with boot_ps NULL. radeon_pm_init_dpm (error swallowed per DF-4162) calls sumo_dpm_enable, which unconditionally ends with sumo_update_current_ps(rdev, boot_ps) -> sumo_get_ps(NULL) loads NULL->ps_priv at offset 40 -> page-fault panic AFTER DPM started. Disable-path twin reachable on warm reset (enable early-returns, disable later derefs NULL at suspend/unload). Harness-proven (fault at 0x28 == offsetof(ps_priv); fixed harness refuses). radeon.ko builds with all four sumo fixes -Werror.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4187 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | verbatim sumo_dpm.c replica, mode 4187 = BOOT-less VBIOS -> enable -> NULL->ps_priv (0x28) | 34.8 KB | view raw |
| build.sh | build-script | cc -O2 -Wall; also builds harness_fixed (-DFIX_ALL) | 65 B | view raw |
| run.sh | run-script | all modes, baseline + fixed | 120 B | view raw |
| build.log | build-log | final successful builds (guest cc 8.3) | 1018 B | view raw |
| run.log | run-log | decisive run: baseline exit 99/98/99/99, fixed 0/0/0/0 | 3.5 KB | view raw |
| fix.diff | suggested-fix | boot_ps NULL guards in sumo_dpm_enable + sumo_dpm_disable | 817 B | view raw |
| fix_combined.diff | suggested-fix | all four sumo fixes (DF-4187..4190) in one diff | 3.3 KB | view raw |
| radeon_fix_apply.log | apply-log | patch(1) hunk application on guest /usr/src | 808 B | view raw |
| radeon_module_build.log | fix-build-log | radeon.ko module build with fixes, -Werror, MAKE_RC=0 | 139.5 KB | view raw |
| env.txt | environment | guest uname, cc version, no radeon loaded | 568 B | view raw |
| VERDICT.md | verdict | full narrative | 4.4 KB | β raw |
DF-4187 β VERDICT
Status: REPRODUCED (logic-level harness; kernel-level repro hardware-gated) Impact: kernel page-fault panic at drm attach / DPM enable Confidence: certain
Root cause
sumo_parse_pplib_non_clock_info() sets rdev->pm.dpm.boot_ps = rps only
when a VBIOS power state carries ATOM_PPLIB_CLASSIFICATION_BOOT
(sumo_dpm.c:1424-1427). Nothing validates that any state has that class, and
sumo_parse_power_table() still returns 0 (sumo_dpm.c:1526-1527), so
sumo_dpm_init() succeeds with boot_ps == NULL.
The failure is then amplified by the family hub bug DF-4162
(radeon_pm.c:1448 discards radeon_dpm_init()'s return), but even without
the swallow a BOOT-less table alone is sufficient:
radeon_pm_init_dpm() (radeon_pm.c:1453) calls radeon_dpm_enable() β
sumo_dpm_enable() unconditionally ends with
sumo_update_current_ps(rdev, rdev->pm.dpm.boot_ps); /* sumo_dpm.c:1235 */
β sumo_get_ps(NULL) (sumo_dpm.c:1186 via :77-79) loads
NULL->ps_priv at offset 40 (radeon.h:1342-1356) β page-fault panic
after DPM has already been started (sumo_start_dpm, :1228) β the GPU is
left with DPM enabled and half-programmed state.
The disable-side twin is also live: with a warm-reset box whose hardware
already reports DPM_SCLK_ENABLE, sumo_dpm_enable() early-returns -EINVAL at
:1209-1210 (no crash), and the later sumo_dpm_disable() proceeds to
sumo_update_current_ps(rdev, boot_ps) at :1280 β same NULL deref at
suspend/unload.
Trigger / threat model
Crafted or truncated VBIOS AtomBios PowerPlayInfo table on a Sumo/Ontario
(SUMO/SUMO2), Palm or Wrestler APU with radeon.dpm enabled (default for
IGP): a power table whose states all lack the BOOT classification bit.
Attacker-controlled VBIOS = flashed card / replaced OPROM / VM GPU ROM
passthrough. Result: deterministic kernel panic at driver load (or at first
suspend for the disable path).
Harness proof (mode 4187)
Verbatim replica of sumo_parse_power_table, sumo_parse_pplib_*,
sumo_patch_boot_state, sumo_update_current_ps, sumo_get_ps and the
sumo_dpm_enable skeleton (register-only sub-programming stubbed β see
harness.c header). Crafted 16 KB VBIOS image with one PERFORMANCE-classified
state:
sumo_parse_power_table() = 0 num_ps=1 boot_ps=0x0 <- parse "succeeds", boot_ps NULL calling sumo_dpm_enable() with boot_ps == NULL ... *** SIGNAL 11 at address 0x28 *** <- offsetof(radeon_ps, ps_priv) = 40 exit=99
The faulting address 0x28 matches the kernel field offset exactly β this is
the NULL->ps_priv load itself, i.e. the kernel page-fault at
sumo_dpm.c:1186/1235.
Stubs that do not affect the result: WREG32/RREG32 (no-ops),
r600_calculate_u_and_p (no-op), kzalloc/kfree (canary allocator β irrelevant
here), le16/le32 (identity), atom_parse_data_header (image crafted with the
same table-offset struct).
Fix validation
- Harness fixed build (
-DFIX_DF4187:boot_ps == NULLguard at the top ofsumo_dpm_enableand after the hw check insumo_dpm_disable):harness_fixed 4187β "no boot power state, refusing to enable DPM", exit 0, no signal. (run.log, fixed section.) fix.diff(both guards) applied to the guest's/usr/srccopy together with the other three sumo fixes (fix_combined.diff): fullradeon.komodule build under-WerrorβMAKE_RC=0,sumo_dpm.o+radeon.koproduced (radeon_module_build.log,radeon_fix_apply.loghunks 1-2).
Kernel-on-hardware validation is not possible on the audit guest (no ATI APU;
kldstat | grep -c radeon = 0).
Kernel references
- sys/dev/drm/radeon/sumo_dpm.c:1235 (the deref, enable path)
- sys/dev/drm/radeon/sumo_dpm.c:1186, :77-79 (NULL->ps_priv load)
- sys/dev/drm/radeon/sumo_dpm.c:1280 (disable-path twin)
- sys/dev/drm/radeon/sumo_dpm.c:1424-1427 (boot_ps only set for BOOT class)
- sys/dev/drm/radeon/sumo_dpm.c:1526-1527 (parse returns 0 regardless)
- sys/dev/drm/radeon/radeon_pm.c:1448 (DF-4162 swallow), :1453 (enable call)
- sys/dev/drm/radeon/radeon.h:1342-1356 (ps_priv at offset 40)
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
fixedharness_fixed 4187 completes cleanly (refuses enable, exit 0); combined fix builds radeon.ko under -Werror on guest /usr/src (MAKE_RC=0)
run.log fixed section; radeon_fix_apply.log; radeon_module_build.log tail 'MAKE_RC=0', sumo_dpm.o + radeon.ko produced
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log '===== mode 4187 =====' (baseline): SIGSEGV at 0x28; fixed section: 'no boot power state, refusing to enable DPM', exit=0; radeon_module_build.log MAKE_RC=0
PoC changes
wrote harness from scratch (no seed): verbatim function replicas + canary allocator + crafted VBIOS image; register-only plumbing stubbed
Verified recommended fix
guard rdev->pm.dpm.boot_ps == NULL in sumo_dpm_enable() and sumo_dpm_disable(); honor dpm.init failures in radeon_pm.c (DF-4162 hub)
Verdict
Reproduced at logic level with a verbatim userspace replica of the sumo code paths (harness mode 4187): a BOOT-class-less VBIOS power table parses successfully with boot_ps==NULL, and sumo_dpm_enable() (:1235) -> sumo_update_current_ps(rdev, NULL) -> sumo_get_ps(NULL) faults on NULL->ps_priv at offset 0x28 == offsetof(radeon_ps, ps_priv) β the exact kernel page-fault. Kernel-on-hardware repro is hardware-gated (no Sumo/Palm APU on the audit guest); DPM is enabled by default on these IGPs and radeon_pm.c:1448 (DF-4162) swallows init failures, but the BOOT-less-table path alone suffices. Fixed build refuses to enable (exit 0); fix also proven to compile in-kernel (radeon.ko, -Werror, MAKE_RC=0).
No comments yet.