# DF-5151 — dm pdev dedup key silently truncated to 31 chars → wrong-device table binding

* File audited (pass 2): `sys/dev/disk/dm/linear/dm_target_linear.c` (root cause in
  `sys/dev/disk/dm/dm_pdev.c`, reached through the linear target's unvalidated
  `argv[0]` device path).
* Severity: Low (privileged local — table loads go through `NETBSD_DM_IOCTL` on
  `/dev/mapper/control`, 0640 root:operator; the group cannot usefully drive it
  because the pdev `vn_open(FREAD|FWRITE)` runs under the caller's credentials).
* Verdict: **reproduced** on the stock 6.5-DEVELOPMENT guest (two independent
  boots, incl. a fresh `vm.sh reset with-src`). Fix validated on a rebuilt
  `dm.ko`.

## Build

```
./build.sh        # cc -O2 -Wall -o poc_dm_pdev_trunc poc_dm_pdev_trunc.c
```
(no libprop needed — the plist XML is hand-built; only `<sys/ioctl.h>`)

## Run (as root on the guest)

```
sh run.sh                       # baseline: kldload dm; kldload dm_target_linear
```

Setup created by run.sh:

```
T = /tmp/dmtr/aaaaaaaaaaaaaaaaaaaaa    (exactly 31 chars) -> symlink to /dev/md0   (Y)
L = /tmp/dmtr/aaaaaaaaaaaaaaaaaaaaabb  (33 chars)        -> symlink to /dev/md0s0 (X)
```

What the PoC does:

1. `create` dm devices dfa/dfb/dfc
2. `reload` dfa with linear params "`<L> 0`" — kernel opens X (md0s0) and stores
   the pdev under `strlcpy(name, L, 32)` = **T** (truncated)
3. `reload` dfb with linear params "`<T> 0`" — T resolves to Y (/dev/md0), but
   `dm_pdev_lookup_name(T)` strcmp-matches the truncated stored key → dfb
   silently reuses dfa's pdev **without ever opening T**
4. control: `reload` dfc with a nonexistent path → must FAIL (normal behavior)
5. `resume` dfa/dfb (tables load into the inactive slot), then `deps` both

## Expected output (stock kernel — the bug)

```
expected deps(dfb) if correct = 0x1e110007     <- dev_t of /dev/md0
reload dfc (...): ioctl FAILED with errno=2    <- control behaves normally
==== RESULT ====
deps(dfa [path L -> /dev/md0s0]) = 0x1e100007  <- md0s0, correct for dfa
deps(dfb [path T -> /dev/md0   ]) = 0x1e100007  <- WRONG: dfb got md0s0's pdev!
MISDIRECTION PROVEN (BUG PRESENT): ...
poc exit=0
```

`/dev/md0` is `0x1e110007`, `/dev/md0s0` is `0x1e100007` (see `ls -l` in run.log).
Every IO issued to dm device dfb lands on `/dev/md0s0` even though its table was
loaded with a path that resolves to `/dev/md0`.

## Expected output (patched dm.ko — fix validated)

```
reload dfa (/tmp/dmtr/aaaaaaaaaaaaaaaaaaaaabb): ioctl FAILED with errno=2
dfa reload with 33-char path was REJECTED by kernel
deps(dfb [path T -> /dev/md0   ]) = 0x1e110007   <- correct device now
FIX CONFIRMED: long path rejected loudly, short path bound to its own device
```

Console/dmesg evidence of the loud rejection:
`dm_pdev_insert: device path too long (max 31): /tmp/dmtr/aaaaaaaaaaaaaaaaaaaaabb`

Fix rebuild + run:
```
cd /usr/src && patch -p1 < /tmp/poc5151/fix.diff
cd /usr/src/sys/dev/disk/dm && make obj && make          # -> dm.ko (-Werror clean)
cd /tmp/poc5151 && DM_KO=/usr/obj/usr/src/sys/dev/disk/dm/dm.ko sh run.sh
```

## Artifacts

* `poc_dm_pdev_trunc.c` — PoC source (hand-built proplist XML ioctls)
* `build.sh`, `run.sh` — exact commands
* `run.log` — decisive baseline run (dirty-boot guest)
* `run.2.log` — baseline run on a FRESH `vm.sh reset with-src` boot (deterministic)
* `fix.diff` — git-apply-able fix (bounds check in `dm_pdev_insert`)
* `fix_build.log`, `fix_run.log` — patched-module build + verification run
* `env.txt` — guest uname + device list
