# DF-4759 — vinum module teardown has no real synchronization: MOD_UNLOAD TOCTOU vs vinumopen, plus the `state > volume_down` gap skips open-but-down volumes (UAF-class teardown race; privileged parties)

* Files: `sys/dev/raid/vinum/vinum.c:171-189` (`vinum_inactive`),
  `vinum.c:295-358` (`vinum_modevent` MOD_UNLOAD), `vinum.c:198-259`
  (`free_vinum`), racing `vinum.c:372-476` (`vinumopen`) and the strategy
  path.
* Severity: Low (all racing parties are privileged: kldunload needs root;
  volume/plex/sd device nodes are 0640 root:operator — operator ≈ raw-disk
  access ≈ root).
* Status: **untested** (analysis only — nondeterministic privileged race;
  the same unlocked-teardown class was guest-confirmed by the sibling
  finding DF-4478 on vinumstate/vinumioctl).

## What

`MOD_UNLOAD` decides it is safe to free everything with two
non-atomic heuristics, then frees `DRIVE/SD/PLEX/VOL` and `bzero`s
`vinum_conf` with **no lock held for the duration**:

1. **TOCTOU**: `vinum_inactive(1)` returns "inactive", and only afterwards
   can another process `open()` a vinum node; `vinumopen`
   (e.g. VOLUME/RAWSD paths, vinum.c:386-455) then writes
   `vol->flags |= VF_OPEN` / `sd->flags |= VF_OPEN` into arrays that
   `free_vinum()` may concurrently `Free()` (vinum.c:213-256) — a
   write-after-free window between the check and the frees, and a
   use-after-free for any strategy/close issued on the fd afterwards.
   (The open side also takes no `lock_config`.)

2. **State gap**: `vinum_inactive` only counts a volume as open when
   `VOL[i].state > volume_down && (VOL[i].flags & VF_OPEN)`
   (vinum.c:181-182). A volume that was opened while `up` and has since
   gone `volume_down` (drive error, `vinum set state down`) keeps its open
   fd and `VF_OPEN` flag but is skipped by the check → `kldunload`
   proceeds to free the arrays under the live fd.

3. **Flag ≠ refcount**: `VF_OPEN`/`VF_DAEMONOPEN` are booleans set on
   every open and cleared on the *first* close (vinum.c:398/453 vs
   502/521/534/543-545) — two open superdev/controld fds + one close
   leaves the modules' "is anything open?" answer wrong in both
   directions (see also DF-4760's use of the same flag).

## Why Low / not guest-verified

Both sides of every interleaving are privileged (root for kldunload and
the superdev; operator for volume/rawplex/rawsd nodes — operator has raw
disk R/W on this system class). The window is small and the family-class
outcome (tearing/UAF) is already guest-demonstrated by DF-4478. Filing as
Low with the concrete gaps cited; a deterministic PoC would need a
purpose-built driver harness.

## Recommended fix

Hold `lock_config()` across `vinum_inactive()`'s *decision* and the whole
of `free_vinum()`/dev teardown in MOD_UNLOAD; make `vinumopen` take
`lock_config()` around its state test + `VF_OPEN` set; change the
open-check to `(VOL[i].flags & VF_OPEN)` regardless of state (or keep
per-object open refcounts instead of boolean flags).
