# DF-2448 — `dm_table_load_ioctl` NULL-deref via missing `DM_IOCTL_CMD_DATA`

## Claim
`dm_table_load_ioctl` (sys/dev/disk/dm/dm_ioctl.c:673) fetches
`DM_IOCTL_CMD_DATA` with `prop_dictionary_get` (returns NULL when key absent)
and immediately passes the result to `prop_array_iterator()`. In kernel
proplib, `prop_array_iterator` does `_PROP_RWLOCK_RDLOCK(pa->pa_rwlock)` ==
`mtx_lock(&(pa->pa_rwlock))` BEFORE any NULL check → NULL-deref panic
when the `cmd_data` key is missing/not-an-array.

## Reproduce
```sh
./build.sh                   # cc -o dm_nulldata_deref dm_nulldata_deref.c -lprop
ssh dfbsd 'kldload dm'       # root only; creates /dev/mapper/control (0640 root:operator)
ssh dfbsd 'cd poc/DF-2448 && ./dm_nulldata_deref'
```

## Expected (bug present, unpatched `dm.ko`)
Kernel panic, guest drops to DDB, ssh dies:
```
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x40
Stopped at      prop_array_iterator+0x1b:  lock cmpxchgl %edx,0x40(%rdi)
db>
```

## Expected (after fix.diff applied, patched `dm.ko`)
No panic; PoC prints:
```
[!] reload ioctl returned rv=22 (Invalid argument) -- kernel survived?
```
Guest stays up; can be re-run any number of times.

## Privilege
The dm module must be `kldload`-ed by root, and `/dev/mapper/control` is
`0640 root:operator`. So this bug is reachable only by root or
`operator`-group members. It is a **root/operator → kernel DoS** /
hardening gap, NOT an unprivileged → root escalation. The primitive is a
NULL-deref page fault (no write), so there is no escalation chain.

## Files
* `dm_nulldata_deref.c` — PoC source (libprop NETBSD_DM_IOCTL).
* `build.sh` / `run.sh` — build/run scripts.
* `fix.diff` — verified fix (NULL check on cmd_array + iter).
* `VERDICT.md` — full mechanism walkthrough + Phase 8 validation.
