# DF-4493 — VERDICT

**Finding**: NULL boot_ps / never-parsed states (`ucStateEntrySize == 1` or
missing BOOT classification) → NULL-pointer page-fault panic in the rs780
DPM enable/print paths.

**Status: REPRODUCED at logic level (verbatim replica), 2 attempts.**
Kernel-level trigger hardware-gated (no RS780/RS880 IGP in the QEMU guest);
family precedent DF-4187/DF-4124/DF-4143/DF-4118 verified identically.

## Reachability chain (all cited)

1. `radeon_pm_init_dpm()` — `radeon_dpm_init(rdev);` at radeon_pm.c:1448
   is a bare statement: **the return value of `rs780_dpm_init()` is
   discarded**, so parse "success" is not even required — but in these two
   scenarios the parse genuinely returns 0 (rs780_dpm.c:843).
2. radeon_pm.c:1449 `current_ps = requested_ps = boot_ps` (NULL).
3. radeon_pm.c:1453 `radeon_dpm_enable(rdev)` → rs780_dpm.c:600
   `boot_ps = rdev->pm.dpm.boot_ps` (NULL) → :608
   `rs780_initialize_dpm_parameters(rdev, NULL)` → :78
   `rs780_get_ps(NULL)` → `rps->ps_priv` load from **NULL+40 (0x28)**.

Variant B (`ucStateEntrySize == 1`): rs780_dpm.c:822
`if (power_info->pplib.ucStateEntrySize - 1)` evaluates `1 - 1 == 0` → the
entire per-state parse (kzalloc igp_ps, ps_priv attach, boot_ps assignment
at :741-742) is skipped, while :842 still sets `num_ps = ucNumStates` and
the function returns 0. Consumers then deref the NULL ps_priv:
`rs780_dpm_print_power_state()` :946 (`ps->sclk_low`, NULL+8),
`rs780_dpm_set_power_state()` :567-568, `rs780_dpm_get_sclk()` :965-970,
`rs780_dpm_force_performance_level()` :1031.

## Observed (run.log)

```
===== mode 4493 =====
rs780_parse_power_table() = 0 num_ps=1 boot_ps=0x0
calling rs780_dpm_enable() with boot_ps == NULL ...
*** SIGNAL 11 at address 0x28: SIGSEGV ... (kernel: page-fault PANIC) ***
exit=99

===== mode 4493b =====
rs780_parse_power_table() = 0 num_ps=1 boot_ps=0x0 ps[0].ps_priv=0x0
radeon_dpm_print_power_states() walks 1 states whose ps_priv are NULL ...
*** SIGNAL 11 at address 0x8: SIGSEGV ... ***
exit=99
```

Address 0x28 is exactly `offsetof(struct radeon_ps, ps_priv)` (verified in
the harness against the kernel layout radeon.h:1342-1357); address 0x8 is
`offsetof(struct igp_ps, sclk_low)` (rs780_dpm.h:52-58). The harness is
compiled with `-fno-delete-null-pointer-checks` — the same semantics the
kernel relies on — so the loads trap instead of being folded away.

## Fix validation

`fix.diff`: (a) `rs780_dpm_enable()` returns `-EINVAL` when
`boot_ps == NULL`; (b) parse rejects `ucStateEntrySize < 2`. Fixed harness:

```
DRM_ERROR: no boot power state, refusing to enable DPM
rs780_dpm_enable() returned -22 - NO CRASH (fix active)   exit=0
DRM_ERROR: invalid state entry size 1
rs780_parse_power_table() = -22 ... crafted table rejected   exit=0
```

Applied to the guest `/usr/src`: radeon_fix_apply.log (7 hunks),
radeon_module_build.log — `radeon.ko` links under **-Werror**, MAKE_RC=0.
Guest source reverted (md5 verified) and guest reset afterwards.

## Attempts

1. First run showed "NO CRASH": the userspace stub's dead outputs let GCC
   eliminate the NULL loads (UB folding). Fixed by compiling with
   `-fno-delete-null-pointer-checks` and feeding the loaded sclk into a
   volatile sink — restoring kernel trap semantics.
2. Decisive run in run.log.

## 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.
