# DF-5057 VERDICT — mlxd detach/delete lifetime TOCTOU + no in-flight drain

**status: untested (hardware-gated) | impact: none demonstrated (memcorrupt class) | confidence: certain (code trace)**

## Reproduced?

No. Requires a physical Mylex DAC960 family adapter (PCI vendor 0x1069, or
0x1011:1065 for the type-5 interface, mlx_pci.c:80-83). Guest evidence
(env.txt, live run): no such PCI function exists, `/dev/mlx*`/`/dev/mlxd*`
absent, yet the driver is compiled into the running X86_64_GENERIC kernel
(`device mlx` at X86_64_GENERIC:134; `kldload mlx` → already loaded). Same
hardware-gated situation the family accepted for DF-5046 (ida) and DF-5048.

## Why the finding is real (trace)

The delete paths and their gates:

1. **MLX_DETACH_DRIVE** (mlx.c:774-804): checks `mlxd->mlxd_flags &
   MLXD_OPEN` at **mlx.c:782**, then calls `mlx_flush(sc)` at **mlx.c:788**
   *before* `device_delete_child` at **mlx.c:794**.
   `mlx_flush` (mlx.c:1516-1549) runs `mlx_poll_command` (mlx.c:1677-1703)
   which **busy-spins polling the controller for up to 15,000,000
   iterations** (mlx.c:1690-1694) — a window of milliseconds to seconds,
   during which the controller is NOT marked shut down: `MLX_STATE_SHUTDOWN`
   is only set by `mlx_shutdown` (mlx.c:599), never by MLX_DETACH_DRIVE.
2. **mlxd_open** (mlx_disk.c:86-115) has exactly one gate —
   `sc->mlxd_controller->mlx_state & MLX_STATE_SHUTDOWN` at
   **mlx_disk.c:98** — which is *clear* during the entire MLX_DETACH_DRIVE
   window, so a concurrent open succeeds and sets `MLXD_OPEN`
   (mlx_disk.c:113) *after* the check at mlx.c:782 has already passed.
   No lock is held by either side (`crit_enter()` in mlx.c:780? — none;
   MLX_DETACH_DRIVE runs with no crit/lock at all).
3. **mlxd_detach** (mlx_disk.c:290-301) then runs `devstat_remove_entry` +
   `disk_destroy` with **no MLXD_OPEN recheck and no drain** of bios
   already queued on the controller (`sc->mlx_bioq`, set by
   mlxd_strategy → mlx_submit_bio at mlx_disk.c:180 / mlx.c:688) or of
   commands already in the controller's `mlx_work` queue.
4. `disk_destroy` (subr_disk.c:545-549 → DISK_DISK_DESTROY at
   subr_disk.c:873-897) only interlocks against *disk-enumeration* refs
   (`dp->d_refs`); the cdev teardown (`destroy_dev`) is asynchronous and
   the open descriptor keeps the cdev alive with `si_ops = mlxd_ops` and
   `si_drv1 = sc` (set at mlx_disk.c:259-260) **dangling into freed
   memory** once newbus frees the mlxd_softc after detach — the exact
   mechanism established for this family in DF-5046 (same disk_create/
   devfs teardown: subr_disk.c:875-881, devfs_core.c:759-763,
   devfs_core.c:1440-1459, kern_device.c:355-389).
5. Post-free touch points: `mlxd_close` writes `sc->mlxd_flags &=
   ~MLXD_OPEN` (mlx_disk.c:127); `mlxd_strategy` reads
   `sc->mlxd_drive`/`mlxd_controller` and calls
   `devstat_start_transaction(&sc->mlxd_stats)` + submits with freed
   pointers (mlx_disk.c:161,173,179-180); `mlxd_ioctl` dereferences
   `sc->mlxd_controller` (mlx_disk.c:143); and a late controller
   completion for an in-flight bio runs `mlx_completeio` (mlx.c:1811-1841)
   → **`mlxd_intr` → `devstat_end_transaction_buf(&sc->mlxd_stats, bp)`**
   (mlx_disk.c:204) — a *write* into freed kernel memory.
6. **Second leg:** `mlx_shutdown` (mlx.c:611-617) deletes *all* children
   unconditionally — the MLXD_OPEN pre-check exists only in `mlx_detach`
   (mlx.c:558-566), not in `mlx_shutdown` itself. `mlx_pci.c:54` wires
   `mlx_shutdown` as `device_shutdown`, so the **system reboot/shutdown
   path deletes every mlxd child under open descriptors and in-flight
   bios** with no gate at all.

## Not a duplicate

* DF-2074 (this file) = stale `bio_driver_info` in strategy — different
  root cause (no device lookup), no detach involvement.
* DF-1303/DF-1304/DF-3949–DF-3952 (mlx.c) = firmware-bound trust, DCDB
  fixup, NEXT_CHILD NULL panic, wait-command loop, wedge-path slot leak,
  dmamap panic — none covers the child lifetime/TOCTOU.
* DF-5013/DF-5025/DF-5046/DF-5049 = same family defect in mfi/ips/ida/mfi-
  syspd; this is the mlx instance (the last LD sibling), with the nuance
  that mlx *has* an open-check but it is check-then-sleep-then-act.

## Severity rationale

Low, matching DF-5025 (ips): initiation requires root (MLX_DETACH_DRIVE on
the 0600 root:operator control node, or reboot). There is no mfi-style
unprivileged event-driven delete in mlx (the periodic enquiry only *prints*
state changes, mlx.c:1126-1154). The reboot leg (leg 2) fires on any machine
with a mounted mlxd root/swap, but requires physical DAC960 hardware.

## Fix assessment

fix.diff: (1) MLX_DETACH_DRIVE raises MLX_STATE_SHUTDOWN for the whole
flush+delete window and clears it at detach_out — closes the open-race
window using mlxd_open's *existing* gate; (2) mlxd_detach rechecks MLXD_OPEN
and returns EBUSY. The remaining in-flight-bio leg needs a drain (wait on
mlx_waitbufs / outstanding commands) before disk_destroy — noted in the
diff header as the complete fix. Cannot be build/boot-validated (no
hardware); diff is git-apply-clean against sys/.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
