# DF-2450 VERDICT — dm_pdev NULL-deref via non-block-device backing path

## Verdict: REPRODUCED (panic) — root→kernel DoS; fix VALIDATED

## Summary

Reloading a dm `linear` table whose backing path resolves to a **non-block-device
vnode** (regular file) triggers a NULL-deref kernel panic in `dm_pdev_insert`
(sys/dev/disk/dm/dm_pdev.c:168).

The finding's description said "path does NOT resolve (e.g. /dev/nonexistent)".
That specific case is handled cleanly (dm_dk_lookup fails → dm_pdev_insert
returns NULL → linear target returns ENOENT). The **actual** trigger is a path
that **does** resolve via vn_open but whose vnode is not a device (v_rdev==NULL),
e.g. a regular file. The underlying bug (NULL v_rdev dereferenced in
`dev_dioctl`) is real and is in `dm_pdev.c` as cited.

## Mechanism (trigger → primitive → effect)

1. **Attacker opens `/dev/mapper/control`** (0640 root:operator — root/operator
   only) and sends `create` + `reload` ioctls (libprop NETBSD_DM_IOCTL).
2. The `reload` specifies target type `linear` with params `<path> 0` where
   `<path>` is a regular file (e.g. `/tmp/df2450_backing`).
3. `dm_table_load_ioctl` (dm_ioctl.c:783) calls `dm_target_linear_init`
   (dm_target_linear.c:56).
4. `dm_target_linear_init` calls `dm_pdev_insert(argv[0])` (line 67).
5. `dm_pdev_insert` (dm_pdev.c:120) calls `dm_dk_lookup` (line 143) →
   `vn_open` **succeeds** for the regular file.
6. `dm_pdev_insert` then calls **line 168**:
   ```c
   error = dev_dioctl(dmp->pdev_vnode->v_rdev, DIOCGPART, ...);
   ```
   For a regular-file vnode, `v_rdev` is NULL. `dev_dioctl(NULL, ...)` reads
   `0xa8(%rdi)` where `%rdi==NULL` → **page fault at virtual address 0xa8**.

## Panic signature

```
Fatal user address access from kernel mode from poc at ffffffff8062cd5c
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0xa8
Stopped at dev_dioctl+0xc: movq 0xa8(%rdi),%rax
```

## Exploit chain

N/A — **valid hard blocker (root-only reachability)**. `/dev/mapper/control`
is 0640 root:operator; maxx (uid 1001) is not in operator/wheel. The bug
requires root to trigger. There is no unprivileged path. NULL-deref is not a
write primitive, so even from root there is no escalation chain. This is a
root→kernel DoS / hardening gap.

## Fix

Add a NULL check for `v_rdev` in `dm_pdev_insert` after `dm_dk_lookup` succeeds
(dm_pdev.c:151), rejecting non-device vnodes cleanly (returns NULL → caller
returns ENOENT). See `fix.diff`.

## Fix validation

- **Unpatched (#0 baseline):** PoC panics the kernel (Fatal trap 12,
  `Stopped at dev_dioctl+0xc`).
- **Patched (fixed dm.ko):** Same PoC returns `reload rv=2 (ENOENT)` —
  clean rejection, no panic. Guest stays up.

Fix validated by rebuilding only dm.ko (`make` in `sys/dev/disk/dm`),
installing to `/boot/kernel/dm.ko`, `kldunload`/`kldload dm`, re-running
the same PoC.
