Broken attach-unwind contract: mps_attach leaks sc->facts on early failures, and mps_pci_free destroys the parent DMA tag over live child tags when mps_free early-returns
Summary
mps_attach returns error without unwind at two sites (facts kmalloc leak), and whenever mps_free early-returns (exactly when the IOC is wedged) mps_pci_free destroys mps_parent_dmat over six live child tags - bus_dma_tag_destroy has no parent-child refcount, so dmat->parent dangles and all queue DMA memory leaks. HW-gated (no SAS2008 on guest); unconditional control flow, source-proven. Free+NULL fix + NULL guards riding with DF-2044.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5014 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.3 KB | β raw | |
| VERDICT.md | β | 2.9 KB | β raw | |
| fix.diff | β | 839 B | view raw | |
| manifest.json | β | 1018 B | view raw | |
| verdict.json | β | 3.0 KB | view raw |
DF-5014 β mps attach unwind: sc->facts leak and parent DMA tag destroyed over live children
What
The unwind contract between mps_pci_attach() (sys/dev/raid/mps/mps_pci.c:234-237)
and mps_attach() (sys/dev/raid/mps/mps.c:1024-1215) is broken on two families
of early-failure paths β beyond the known DF-2044 (never-allocated IRQ release,
not re-reported here):
-
sc->factsleaked βmps_attach()returns error without callingmps_free()at: - mps.c:1043-1044 (mps_get_iocfactsfails: IOC wedged / not answering), - mps.c:1072-1073 (mps_transition_readyfails after the diag reset forced by missing EVENT_REPLAY capability). Thekmalloc(sizeof(MPI2_IOC_FACTS_REPLY))from mps.c:1041-1042 is never freed;mps_pci_free()does not know about it. -
parent DMA tag destroyed over live child tags β whenever
mps_free()bails out early (mps.c:1323-1325mps_detach_sas/mps_detach_logerror, mps.c:1331-1334mps_transition_readyerror β realistic exactly when the IOC is wedged, e.g. after mps.c:1128-1133 transition-operational failure), the queue/request/reply/sense/chain/buffer DMA tags and their memory stay allocated while control returns tomps_pci_attach(), whosemps_pci_free()then destroysmps_parent_dmat(mps_pci.c:289-291).bus_dma_tag_destroy()(sys/platform/pc64/x86_64/busdma_machdep.c:334-346) keeps no parentβchild reference count: the parent tag memory is freed silently, leaving every child tag with a danglingdmat->parentpointer (latent UAF of the tag object) and leaking all queue DMA memory (map_counton the parent is 0, so the EBUSY guard never fires).
Both fire only at boot/hotplug on failing hardware (wedged or absent IOC mid-attach) β resource-leak / latent-dangling-pointer class, no attacker- controlled input. Same family context: DF-2044 (known), DF-4868/4869, DF-4363.
Reproduction status
HW-gated: no LSI SAS2008-class controller on the audit guest, mps cannot attach; both defect paths are unconditional control flow (cited above), no runtime PoC possible on this guest.
Fix
fix.diff: free sc->facts (and NULL it) on the two early-error returns in
mps_attach(). DF-2044's NULL-guard for the IRQ teardown in mps_pci_free()
should ride along in the same commit (not duplicated here to keep this diff
scoped to the new finding).
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)
mps_pci_attach()(mps_pci.c:182-238) allocates the register resource and the parent DMA tag, then callsmps_attach(); every non-zero return funnels intomps_pci_free()(mps_pci.c:234-237).mps_attach()returns error without unwinding at exactly two sites: - mps.c:1043-1044:mps_get_iocfacts()failure βsc->factswas 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-forcedmps_diag_reset()β same leak.mps_free()early-returns (leaving all queue DMA tags + memory allocated): - mps.c:1323-1325 (mps_detach_log/mps_detach_sasfailure), - mps.c:1331-1334 (mps_transition_readyfailure β precisely the state reached when the IOC is wedged, e.g. after the transition-operational failure at mps.c:1128-1133). Control then returns tomps_pci_attach()βmps_pci_free()βbus_dma_tag_destroy(sc->mps_parent_dmat)(mps_pci.c:289-291).bus_dma_tag_destroy()(busdma_machdep.c:334-346) has no parent-child refcount β it checks onlymap_count(which counts maps created from this tag; the child tags' maps count against the children). The parent tag memory is kfree'd whilequeues_dmat/req_dmat/reply_dmat/sense_dmat/chain_dmat/buffer_dmatstill reference it asdmat->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.
Fix verification
not_testablefix.diff (git apply --check clean) frees sc->facts at both early-error sites. Single-fix kernel build not meaningful: entry is hardware-gated (no SAS2008 on guest); fix validated by inspection.
fix.diff; VERDICT.md 'Fix' section
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md mechanism section with the four cited sites; the leak paths need no concurrency - only a failing IOC at attach
PoC changes
authored from source analysis; no runnable PoC possible (no mps hardware on guest)
Verified recommended fix
kfree sc->facts (and NULL it) on the two early-error returns in mps_attach() - see fix.diff; ride along with DF-2044's NULL guards in mps_pci_free.
Verdict
Hardware-gated: no LSI SAS2008/MPT2 controller on the audit guest, so mps_attach cannot run; both defect paths are unconditional control flow in the source. (1) mps_attach() returns error without any unwind at mps.c:1043-1044 and mps.c:1072-1073, leaking the sc->facts allocation made at mps.c:1041-1042 (mps_pci_free does not free it). (2) Whenever mps_free() early-returns (mps.c:1323-1325, 1331-1334 - realistic exactly when the IOC is wedged, e.g. after the transition-operational failure at mps.c:1128-1133), all queue DMA tags and memory remain allocated while mps_pci_attach's unwind calls mps_pci_free() -> bus_dma_tag_destroy(mps_parent_dmat) (mps_pci.c:289-291); bus_dma_tag_destroy (busdma_machdep.c:334-346) has no parent-child refcount, so the parent tag is silently freed over live child tags (dangling dmat->parent in six live kernel objects) and all queue DMA memory leaks. Boot/hotplug-time resource-leak + latent dangling-pointer class; no attacker-controlled input.
No comments yet.