# DF-3931 verdict narrative

## Reproduced?
No (hardware-gated + narrow race).  Confidence: likely — every ingredient is
source-verified on this tree; the mechanism class (self-rescheduling callout
defeating stop; teardown without drain) is proven elsewhere on this guest
(DF-3715 replica demonstrated that a callback in progress when the stop lands
determines whether callout_stop can return, and kern_timeout.c's own comments
state "Request a RESET. ... automatically overrides a STOP").

## Root cause chain (path:line)

1. `mly_periodic` re-arms itself unconditionally at its tail:
   mly.c:1397  `callout_reset(&sc->mly_periodic, MLY_PERIODIC_INTERVAL * hz,
                mly_periodic, sc);`
2. Teardown stops it exactly once, asynchronously-to-the-callback:
   mly.c:530   `callout_stop(&sc->mly_periodic);`   (in mly_shutdown)
   mly_free() (mly.c:677-733) never touches the callout.
   Sequence that escapes: timer dequeues callback on CPU1 → mly_shutdown on
   CPU0 issues callout_stop (callback already INPROG; DFly callout_stop is
   sync, kern_timeout.c:1074-1077) → the sync-stop waiter can be released by
   the callback's own callout_reset, whose "RESET ... overrides a STOP"
   (kern_timeout.c:840-846, 976-980) leaves the callout ARMED after
   mly_shutdown returns → mly_detach proceeds to mly_free() → 1s later
   mly_periodic(sc) runs on freed memory: it walks sc->mly_btl[],
   calls mly_rescan_btl → mly_alloc_command on destroyed queues,
   bus_dmamap_load on destroyed tags, mly_start on unmapped registers —
   use-after-free.
3. In-flight commands are not drained either: mly_shutdown's own XXX
   (mly.c:514-517) concedes status polls can be running at detach.
   mly_free() destroys the CAM sims, frees command packets
   (bus_dmamem_free, mly.c:1768-1772) and tears down the interrupt while
   `mly_busy` may be non-empty; the completion task
   (taskqueue_swi → mly_complete, mly.c:1590) then invokes
   mc_complete (e.g. mly_cam_complete → xpt_done on a CCB whose sim is
   gone, mly_release_command touching freed packets).
4. There is no production timeout at all: MLY_CMD_TIMEOUT handling exists
   only under `#ifdef MLY_DEBUG` (mly.c:119, 322-324, 2971-2991) and even
   then only prints.  mly_cam_action has no XPT_ABORT support (falls to
   default → CAM_REQ_INVALID, mly.c:2160-2163), so a hung command hangs its
   CCB forever and, per (3), blocks safe detach forever instead — pick your
   poison.

## Threat model
Requires admin action (kldunload/detach) or hot-removal on a live mly
controller with I/O/events in flight — privilege-gated, but it converts a
routine management operation into a UAF.

## Impact
Use-after-free of the softc, command packets and DMA machinery after detach:
memory corruption / panic.

## Fix
- callout_terminate() for the periodic (prevents the re-arm override), and
- refuse shutdown/detach while mly_busy/mly_complete are non-empty
  (fix.diff).  A production command timeout would be the deeper fix.

## Fix validation
Not possible on this guest (no mly hardware); fix is mechanical
(termination + EBUSY drain gate) and cannot regress the non-error path.

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