# DF-1639 — NULL-deref panic in `dm_table_load_ioctl` via missing `cmd_data` key

## Summary
`dm_ioctl.c:707` `cmd_array = prop_dictionary_get(dm_dict, DM_IOCTL_CMD_DATA)` returns
NULL when the `cmd_data` key is absent from the prop_dictionary.  Line 708
`iter = prop_array_iterator(cmd_array)` is then called with NULL, which dereferences
`NULL->pa_rwlock` (offset 0x40) → `Fatal trap 12: page fault` at
`prop_array_iterator+0x1b`.  **Immediate kernel panic; no device or table needed.**

## Severity / impact
- **Severity filed:** High
- **Verified impact:** `panic` (local DoS).  Read-only NULL-deref at fixed address
  0x40 — there is **no write primitive** and thus no escalation path.  This is a
  valid hard blocker (read-only primitive at a fixed low address).
- **Trigger credential:** operator group (default `/dev/mapper/control` is
  `0640 root:operator`; operator opens O_RDONLY and the ioctl is accepted).
- **Precondition:** admin has loaded the `dm` KLD module (`kldload dm`).  This is a
  realistic admin action (LVM2 / dm-crypt infrastructure).  The bug trigger itself
  is issued by the unprivileged operator-group user.

## Reproduce
```sh
# admin one-time setup
kldload dm
pw groupmod operator -m <user>     # give operator-group users access

# as the operator-group user
./build.sh
./run.sh
# expected (BUG): kernel panics, guest dies, "Fatal trap 12 ... prop_array_iterator"
# expected (FIXED): ioctl returns EINVAL, guest stays up
```

## PoC
`dm_poc.c` case `1639`: sends `command=reload` with version `[4,1,0]` but **no**
`cmd_data` array in the plist dictionary.

## Mechanism (line-accurate)
1. `device-mapper.c:267` `prop_dictionary_copyin_ioctl` deserializes the user plist.
2. `device-mapper.c:270` `dm_check_version` passes (version is `[4,1,0]`, kernel
   wants major==4, minor<=16).
3. `device-mapper.c:271` `dm_cmd_to_fun` dispatches `"reload"` →
   `dm_table_load_ioctl` (`dm_ioctl.c:676`).
4. `dm_ioctl.c:707` `cmd_array = prop_dictionary_get(dm_dict, "cmd_data")` → **NULL**
   (key absent).
5. `dm_ioctl.c:708` `iter = prop_array_iterator(NULL)` →
   `_PROP_RWLOCK_RDLOCK(NULL->pa_rwlock)` → lock cmpxchg at addr 0x40 → page fault.

## Fix
`fix.diff` adds a NULL check for `cmd_array` before the `prop_array_iterator` call,
returning `EINVAL` cleanly when `cmd_data` is missing.

## Fix validation
Built patched `dm.ko` module (all three dm_ioctl.c fixes combined), `kldload`-ed it,
re-ran the PoC: returns `EINVAL` (errno 22), **no panic**, guest stays up.  Baseline
(unpatched module) reproduced the panic every time.
