# DF-3754 — NULL-pointer dereference of `rdev->pm.dpm.boot_ps` / `ps_priv` in ni DPM (crafted VBIOS)

**File:** `sys/dev/drm/radeon/ni_dpm.c` (pass 2)
**Family:** radeon twin of si DF-3610 / ci DF-3646 (amdgpu DF-3586 / DF-3623)

## Build

```
cc -O2 -Wall -o harness harness.c
```

(No AMD NI GPU on the QEMU audit guest — virtio-only — so verification is a
userspace logic harness that executes the exact kernel call graph with
verbatim structs and offsets; see env.txt for the hardware/module check.)

## Run

```
./harness
```

## Expected output

```
[A] ... child died from signal 11: read at NULL+0x28
    >>> kernel: page-fault at 0x28 -> panic during DPM enable <<<
[A2] ... child died from signal 11: read at NULL+0x0
    >>> kernel: page-fault at 0 -> panic on first state change <<<
[B] Fixed path ... rc=-EINVAL (DPM enable refused, no dereference)
```

[A] proves the boot_ps==NULL dereference chain
(`ni_dpm_enable -> ni_init_smc_table -> ni_populate_smc_initial_state(NULL)
-> ni_get_ps(NULL)->ps_priv`, fault address 0x28 = offsetof(struct
radeon_ps, ps_priv) on LP64). [A2] proves the ps_priv==NULL variant
(`ni_update_requested_ps: *new_ps` with new_ps==NULL, fault address 0x0).
[B] shows the fix.diff guard rejecting the configuration instead.

## Fix

`fix.diff` — refuse to enable DPM without a boot state (mirrors the si
DF-3610 fix). Validated: applies cleanly to pristine source (alone and
stacked with DF-3755's fix), compiles `-Werror` clean as a kernel object
(`make KERNBUILDDIR=/usr/obj/usr/src/sys/X86_64_GENERIC ni_dpm.o`, see
fix_build.log), and the harness [B] path shows enable refused with no fault.
