# DF-4751 — dm MOD_UNLOAD: unlocked counter check + unpinned ops dispatch → freed-module-text execution

## Build (guest, as root)
```sh
cc -O -Wall -o /tmp/dm_unloadrace dm_unloadrace.c -lprop -lpthread
```

## Run (as root; /dev/mapper/control is 0640 root:operator)
```sh
kldload dm                       # if not loaded
/tmp/dm_unloadrace 30            # churn vs kldunload hammer
```
Expect a kernel `Fatal trap 12` within seconds-to-a-minute; serial console
(panic.txt) shows the panic; ssh dies; guest is down at the ddb> prompt.

## What it proves
Two compounding windows in `dm_modcmd(MOD_UNLOAD)`
(sys/dev/disk/dm/device-mapper.c:154-169):

1. `if (dm_dev_counter > 0) return EBUSY;` reads the counter with no lock
   while `dm_dev_insert()` increments it under `dm_dev_mutex`
   (sys/dev/disk/dm/dm_dev.c:193) — unload proceeds with live devices
   (console shows `malloc_uninit: 1984 bytes of 'dm' still allocated`).
2. Neither `destroy_dev(dmcdev)` (asynchronous revector via the devfs core,
   sys/vfs/devfs/devfs_core.c:755-765) nor the kld framework pins the module
   against ioctls already dispatched through `dev->si_ops->d_ioctl`
   (sys/kern/kern_device.c:258) — the linker frees module text under the
   running ioctl.

## Fix-validation history (all evidence in this pack)
- fix v1 (destroy_dev first + re-arm on counter>0): still panics — freed
  module text executed (`Stopped at -0x7d9fefd7`, run 3).
- fix v2 (v1 + `sync_devs()`): still panics — `Stopped at dmioctl+0x2bf`
  (run 4). In-driver fixes cannot close a framework-level dispatch window.

## Logs
run.log (churn), panic.txt (all four trap signatures), fix_build.log,
env.txt, fix.diff (v2 — best-effort shrink, NOT sufficient standalone).
