# DF-5230 — NULL `rdev->uvd_fw` dereference in `uvd_v4_2_resume()` via the KV DPM ungate path → kernel panic

**File:** `sys/dev/drm/radeon/uvd_v4_2.c:51` (trigger chain via `sys/dev/drm/radeon/kv_dpm.c:1628`)
**Severity:** Low (hardware+firmware-config gated local DoS) · **Bucket:** base:dev
**Status: reproduced (panic) on the QEMU guest with a synthetic-state harness calling the production function; the production trigger chain is proven statically link-by-link below.**

## What to run

```
./build.sh     # on the guest: build drm.ko, radeon.ko (baseline), df5230.ko
./run.sh       # kldload drm.ko radeon.ko df5230.ko  -> instant panic
```

Expected (serial console / `vm.sh log`):

```
DF5230: pre   uvd_fw=0 fw_header_present=0 gpu_addr=0x0 max_handles=10
DF5230: calling production uvd_v4_2_resume() (radeon.ko) -- expect panic at uvd_v4_2.c:51
Fatal trap 12: page fault while in kernel mode
fault virtual address  = 0x10
Stopped at  uvd_v4_2_resume+0x30:  movq 0x10(%rdx),%rdx
```

`0x10` is `offsetof(struct firmware, datasize)` (`sys/sys/firmware.h:51-56`:
name 8B + data 8B + datasize) — the read at `uvd_v4_2.c:51`
(`rdev->uvd_fw->datasize`) with `uvd_fw == NULL`. The fault happens **before**
the first `WREG32` (`uvd_v4_2.c:52`), so no MMIO is touched.

## Fix validation (same trigger, one-line guard)

1. `vm.sh reset with-src` (clean source)
2. `patch -p1 < fix.diff` in the guest's `/usr/src`, rebuild `radeon.ko`
3. identical `kldload df5230.ko` →

```
DF5230: pre   uvd_fw=0 fw_header_present=0 gpu_addr=0x0 max_handles=10
DF5230: calling production uvd_v4_2_resume() (radeon.ko) -- expect panic at uvd_v4_2.c:51
DF5230: survived, uvd_v4_2_resume returned -22
```

Guest stays up; kldload succeeds. Baseline panicked twice (once on the
original spawn, once from a fresh `vm.sh reset` with source reverted),
identical signature both times (`run.baseline.log`, `panic.txt`,
`run.baseline2.log`, `panic.2.txt`).

## Files

* `df5230.c`, `Makefile` — the harness kld (zeroed `struct radeon_device` =
  the exact post-failed-`radeon_uvd_init()` state; calls the **production**
  `uvd_v4_2_resume` compiled into `radeon.ko`)
* `build.sh`, `run.sh` — exact commands
* `panic.txt`, `panic.2.txt` — serial-console panic transcripts (2 runs)
* `run.baseline.log`, `run.baseline2.log`, `run.fixed.log`, `load_modules.log`
* `fix.diff` — one-line guard (`git apply --check` clean)
* `env.txt` — guest environment
