# DF-4926 — nvmm module unload tears down the hypervisor before the device is dead

`nvmm_dragonfly.c` destroys the NVMM implementation state **while /dev/nvmm
is still live**, inverting the required `destroy_dev()`-first order:

* `nvmm_detach()` (nvmm_dragonfly.c:366-374): unsynchronized
  `nmachines == 0` check at :369, then `nvmm_fini()` at :372 —
  which `lockuninit()`s **all 128×128 machine/vcpu locks** (nvmm.c:998-1003),
  runs `nvmm_impl->fini()` (VMXON regions, MSR bitmaps freed) and sets
  `nvmm_impl = NULL` (nvmm.c:1005-1006).
* `nvmm_modevent()` MOD_UNLOAD (nvmm_dragonfly.c:399-405) calls
  `destroy_dev(dev)` only **after** `nvmm_detach()` has already done all of
  that — the device is open and dispatching ioctls the entire time.

Consequences (all reachable by an unprivileged user in group `nvmm`, given
an admin running `kldunload nvmm`):

1. **In-flight ioctl during `nvmm_fini()`**: `nvmm_capability()`
   (nvmm.c:245,251) dereferences `nvmm_impl` on every call → NULL-page
   kernel deref; any machine ioctl uses `lockuninit`ed locks or freed impl
   state (UAF).
2. **Machine-create racing the `nmachines` check**: a machine allocated
   between :369 and `nvmm_fini()` survives into destroyed-lock land.
3. **Open fd across a successful unload**: `dfbsd_nvmm_dtor()`
   (nvmm_dragonfly.c:325-335) is a module-text function pointer stored in
   the file's cdevpriv; closing the fd after unload calls
   `nvmm_kill_machines()` through unmapped text.

## Build (on the DragonFly guest, as root)

    cc -O2 -pthread -o nvmm_race nvmm_race.c

## Run

As root, end-to-end (creates the `testnv` group-nvmm user if missing,
builds the racer, loads nvmm, races 40 unload/reload cycles, then a final
unload; the racer keeps the fd open for 75 s and closes it at exit):

    ./run.sh                  # panics are expected; watch the serial log

Note: /dev/nvmm is 0640 root:nvmm, so the unprivileged racer opens it
O_RDONLY — DFly does not require FWRITE for ioctls, which is exactly how
an unprivileged group-nvmm user drives every NVMM_IOC_* command.

The guest must be booted with the host CPU exposed so nvmm attaches
(`QEMU_EXTRA="-cpu host" ./vm.sh up`).

## Expected output

* racer `/tmp/race.log`: `t=1ds cap=... create=... destroy=...` counters
* kernel: `Fatal trap 12: page fault while in kernel mode` with
  `nvmm_capability` in the trace (NULL `nvmm_impl`), or a lockmgr/freed-text
  panic from the other detonation paths; guest goes down.
