# DF-5014 — VERDICT

**Status:** NOT REPRODUCED at runtime — hardware-gated (no LSI SAS2008/
MPT2-family controller on the audit guest; `mps` cannot be attached).
Both defect paths are unconditional control flow, line-traced below against
the audited tree. House standard for HW-gated driver findings (DF-2066,
DF-3898): source-proven, runtime-gated.

**Impact ceiling:** resource leak (small one-shot `sc->facts` leak; unbounded
queue-DMA leak + leaked child DMA tags on the mps_free-early-return family)
plus a latent dangling `dmat->parent` in every surviving child tag. No
attacker-controlled input on the path; trigger is a wedged IOC at boot or
hotplug. Low severity, leak class.

## Mechanism (source-proven)

1. `mps_pci_attach()` (mps_pci.c:182-238) allocates the register resource and
   the parent DMA tag, then calls `mps_attach()`; **every** non-zero return
   funnels into `mps_pci_free()` (mps_pci.c:234-237).
2. `mps_attach()` returns error *without* unwinding at exactly two sites:
   - mps.c:1043-1044: `mps_get_iocfacts()` failure — `sc->facts` was
     kmalloc'd at mps.c:1041-1042 one statement earlier; nothing frees it.
     `mps_pci_free()` releases only dmat/irq/regs (mps_pci.c:286-305).
   - mps.c:1072-1073: `mps_transition_ready()` failure after the
     EVENT_REPLAY-forced `mps_diag_reset()` — same leak.
3. `mps_free()` early-returns (leaving all queue DMA tags + memory allocated):
   - mps.c:1323-1325 (`mps_detach_log`/`mps_detach_sas` failure),
   - mps.c:1331-1334 (`mps_transition_ready` failure — precisely the state
     reached when the IOC is wedged, e.g. after the transition-operational
     failure at mps.c:1128-1133).
   Control then returns to `mps_pci_attach()` → `mps_pci_free()` →
   `bus_dma_tag_destroy(sc->mps_parent_dmat)` (mps_pci.c:289-291).
4. `bus_dma_tag_destroy()` (busdma_machdep.c:334-346) has **no parent-child
   refcount** — it checks only `map_count` (which counts maps created from
   *this* tag; the child tags' maps count against the children). The parent
   tag memory is kfree'd while `queues_dmat`/`req_dmat`/`reply_dmat`/
   `sense_dmat`/`chain_dmat`/`buffer_dmat` still reference it as
   `dmat->parent` → dangling pointer in live kernel objects (latent UAF of
   tag memory; benign only because nothing subsequently allocates maps from
   those tags on a failed attach).

## Exploit chain
None (leak class; no attacker-controlled data on the path).

## Fix
`fix.diff` — free `sc->facts` at both early-error returns in `mps_attach()`.
`git apply --check` clean. Single-fix kernel build not meaningful on this
guest (entry hardware-gated); fix validated by inspection.

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