# DF-3755 — dangling `rdev->pm.dpm.ps` + double free + UAF on the alloc-failure path of `ni_parse_power_table`

**File:** `sys/dev/drm/radeon/ni_dpm.c` (pass 2)
**Family:** radeon twin of si DF-3611 / ci DF-3645 (amdgpu DF-3588 / DF-3624)

## 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 replicating the exact lifetime sequence with
malloc/free and the same allocator-reuse semantics kmalloc has; see env.txt.)

## Run

```
./harness
```

## Expected output (decisive lines)

```
[A] ... current_ps = requested_ps = boot_ps = 0x...  (DANGLING: ps array freed at ni_dpm.c:4022)
    num_ps = 0 (never set -> ps_priv[0..4] leak permanently)
[B] ... victim alloc (384 bytes) = 0x...  (reuse: SAME chunk)
    ni_dpm_enable consumed reused-chunk data: mclk=0xdeadbee
[C] ... post-corruption allocations: x1=0x... x2=0x... -> ALIAS (same chunk handed out twice)
[C2] ... victim freed while live; next allocation overlays it
[D] ... rc=-ENOMEM propagated; dpm.ps = NULL; ps_privs freed; no double free, no leak
```

[A] the parse failure frees the state array and the (ignored) init error
lets `radeon_pm_init_dpm` publish the dangling pointer; [B] the UAF read
through `boot_ps` after slab reuse; [C]/[C2] the teardown double free
proven as silent allocator corruption (live alias / victim freed while
live); [D] the fixed path.

## Fix

`fix.diff` — on the kzalloc-failure path free the already-parsed ps_priv
states, NULL `dpm.ps`/`boot_ps`/`uvd_ps` (kills dangling + double free +
leak), and make `ni_dpm_fini` NULL-safe. Validated: applies cleanly (alone
and stacked with DF-3754'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), harness [D] shows the corrected behavior.
