# DF-5013 — VERDICT

**Status:** NOT REPRODUCED at runtime — hardware-gated (no LSI MegaRAID
controller on the audit guest; mfi cannot attach). The defect is
**source-proven end-to-end**: every link in the chain below is unconditional,
cited control flow in the audited tree (same commit the guest kernel was built
from). This is the same standard applied to DF-2066 and DF-3898.

**Impact ceiling:** kernel heap use-after-free (write-after-free via
`devstat_end_transaction_buf` on the freed softc; read + indirect call through
freed `si_drv1` via the disk ops). Realistic weaponization: operator-group
user (raw mfid nodes are root:operator 0640) racing an open fd / in-flight
read against a volume-offline event or LD_DELETE → freed-softc deref →
slab-groom → object forge → uid=0. The race window is the firmware round-trip
of the deleting DCMD (milliseconds) for the ioctl path, and *zero* (no gate)
for the AEN path — an fd that is already open when the AEN delete lands is
enough; the next I/O on it hits the freed softc.

## Chain (all citations from the audited tree)

1. **Softc free site**: `device_delete_child(sc->mfi_dev, ld->ld_dev)`
   (mfi.c:1508 AEN path, mfi.c:2633 LD_DELETE post-path, mfi.c:2644 CFG_CLEAR
   post-path) → `device_detach` (subr_bus.c:2140-2152) →
   `device_set_driver(dev, NULL)` (subr_bus.c:2146) → `kfree(dev->softc, M_BUS)`
   (subr_bus.c:2127-2130). The `struct mfi_disk` is the driver softc
   (mfi_disk.c:80-84, `sizeof(struct mfi_disk)`).
2. **Missing gates**:
   - AEN: mfi.c:1494-1510 — `device_delete_child` with no `mfi_disk_disable`,
     no OPEN check, no drain (the old KASSERT here is commented out).
   - ioctl post: mfi.c:2630-2635 — no re-check between the pre-check
     (mfi.c:2570-2578) and the delete; pre-check's disable is advisory.
   - `mfi_disk_detach`: mfi_disk.c:178-185 — EBUSY only when
     `mfi_keep_deleted_volumes || mfi_detaching` (defaults 0, mfi.c:729-734);
     no in-flight accounting exists to drain even if it wanted to.
3. **UAF consumers**:
   - `mfi_disk_complete` (mfi_disk.c:304-320), reached from
     `mfi_bio_complete` (mfi.c:2092-2115, called under `mfi_io_lock` from
     `mfi_complete`): writes `sc->ld_devstat` inside the freed softc
     (mfi_disk.c:310), reads `sc->ld_disk.d_cdev` (mfi_disk.c:314).
   - any raced-open descriptor: `si_drv1` (set mfi_disk.c:149) still points
     into the freed softc; `mfi_disk_strategy` derefs `sc->ld_controller`
     (mfi_disk.c:264) and `mfi_disk_open/close` take
     `&sc->ld_controller->mfi_io_lock` (mfi_disk.c:202, 219) — lockmgr on a
     pointer read from freed memory. Devfs unlink is asynchronous
     (`destroy_dev` → `devfs_destroy_dev` posts a message, devfs_core.c:755-763),
     and `disk_destroy` only synchronizes the disk-core message
     (subr_disk.c:876-880, :527-556) — neither revokes an fd that already
     holds a cdev reference.
4. **Bios in flight with OPEN clear**: an in-flight bio does not require the
   fd's d_close to have happened (vnode/fp refs), but the final-in-flight /
   AIO / post-umount-buffer windows leave exactly that state; the AEN path
   (1) additionally doesn't care about OPEN at all.

## Exploit chain (design, not exercised — no hardware on guest)

operator (or any user if an admin-configured volume goes offline while they
hold an fd): hold `O_RDONLY` fd on `/dev/mfidN` → trigger offline (pull last
drive of RAID0 / operator LD_DELETE on scratch volume) → AEN delete frees
softc → keep issuing pread()s on the held fd → strategy derefs freed softc →
heap groom (the 0x...-sized softc slab) → forge `ld_controller` pointer /
victim object → `lockmgr`/`devstat` writes give a controlled write primitive
→ uid=0. Deterministic-UAF variant: park a large AIO read, force OFFLINE,
completion path writes into freed softc.

## Fix
`fix.diff` — in-flight accounting (`ld_active`) + DISABLED gate in strategy +
drain-before-destroy in `mfi_disk_detach` + `mfi_disk_disable()` gate on the
AEN delete path. `git apply --check` clean against the tree. Not built as a
single-fix kernel: entry is hardware-gated (no mfi controller on guest), so a
patched-kernel run could not exercise the path — fix validated by inspection
only.

## 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.
