# DF-5013 — mfi LD lifetime: softc freed under open descriptors and in-flight bios

## What
Every path that deletes an mfi logical-disk child device frees the
`struct mfi_disk` softc (newbus: `device_delete_child` → `device_detach` →
`device_set_driver(dev, NULL)` → `kfree(dev->softc)`, sys/kern/subr_bus.c:2127-2130,
:2146) **while the cdev may still be reachable and while bios for that volume
may still be in flight**:

1. **AEN path — no gate at all** (mfi.c:1494-1510, `mfi_decode_evt`,
   MR_EVT_LD_STATE_CHANGE → new_state OFFLINE): `device_delete_child()`
   directly — no `mfi_disk_disable()`, no OPEN check, no drain.
   A volume going offline (drive failure/pull, foreign import, firmware
   decision) frees the softc while userland holds open fds on `/dev/mfidN`
   (root:operator 0640 — sys/kern/subr_disk.c) and/or while its bios sit on
   `mfi_bioq` / the controller busy queue.
2. **ioctl path** (mfi.c:2623-2638, `mfi_check_command_post`,
   MFI_DCMD_LD_DELETE / MFI_DCMD_CFG_CLEAR): the pre-check
   (`mfi_check_command_pre`, mfi.c:2570-2578) calls `mfi_disk_disable()`,
   which refuses when the ld is OPEN — but that flag is advisory and the
   post-path never re-checks; bios already in flight at pre-check time are
   never drained either.
3. `mfi_disk_detach()` itself (mfi_disk.c:171-194): the OPEN check returns
   EBUSY **only** when `mfi_keep_deleted_volumes || mfi_detaching`
   (both default 0, mfi.c:729-734) — i.e. by default detach proceeds even
   with the disk OPEN — and nothing waits for `ld_active`-style in-flight
   accounting because none exists.

After the softc is freed:
- `mfi_disk_complete()` (mfi_disk.c:304-320) later runs for any in-flight bio
  (`bio->bio_driver_info = sc`, set at mfi_disk.c:295) →
  **write-after-free** (`devstat_end_transaction_buf(&sc->ld_devstat, bp)`) and
  read-after-free (`diskerr(bio, sc->ld_disk.d_cdev, ...)`).
- any descriptor that raced open (devfs unlink is asynchronous relative to
  `disk_destroy()`, which itself only synchronizes on the disk-core message:
  subr_disk.c:876-880 → devfs_core.c:755-763) → next `read/write/close` runs
  `mfi_disk_strategy/close` against `si_drv1` pointing into the freed softc.

## Threat model
Raw mfid nodes are `root:operator 0640`. An operator-group user holding
`/dev/mfidN` open (or with AIO in flight) when the volume goes OFFLINE — a
hardware event, no privileged ioctl required — gets kernel heap UAF. The same
operator can drive path 2 directly via MFI_CMD LD_DELETE (0660 on /dev/mfi0,
mfi.c:721-724), sacrificing a scratch volume. Destructive but
single-actor-controlled, and the corruption primitive (freed softc deref by
strategy/completion) is attacker-timing-controlled.

## Reproduction status
HW-gated: the audit guest has no MegaRAID controller, so the mfi attach/lifecycle
cannot run here. The mechanism is source-proven end-to-end (softc kfree site,
completion deref site, missing gates — see VERDICT.md). No runtime PoC possible
on this guest; this matches the DF-2066/DF-3898 house treatment of HW-gated
driver bugs.

## Fix
`fix.diff` (git-apply-able, 3 files):
- `mfivar.h`: `ld_active` in-flight counter (guarded by `mfi_io_lock`).
- `mfi_disk.c`: strategy fails new bios with ENXIO once DISABLED; counts
  in-flight bios; `mfi_disk_complete()` decrements and wakes;
  `mfi_disk_detach()` sets DISABLED and drains (`lksleep`) **before**
  `disk_destroy()`.
- `mfi.c` AEN path: `mfi_disk_disable()` gate before `device_delete_child()`
  (refuse while open), mirroring the ioctl pre-path.
