amr_sglist_map's 'goto retry' leaks the rejected s/g table (and its dmamap) unboundedly and all three PCI-setup bus_dmamap_load results are ignored β load failure yields a 0/garbage bus address handed to the controller
Summary
Each retry overwrites the sg dmamap without unload/free (documented XXX HACK) and the loop is unbounded; the unchecked load means persistent failure pins sgbusaddr at 0 making the retry condition permanently true - infinite leak at attach until kmem exhaustion. The mbox/ccb loads repeat the pattern (failure propagates an uninitialized physical address to the card - practically unreachable but unchecked). Check-loads + free-before-retry + bound fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4708 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 1.8 KB | β raw | |
| fix.diff | β | 2.4 KB | view raw | |
| verdict.json | β | 2.4 KB | view raw |
DF-4708 β amr_pci DMA setup: goto retry loop leaks the s/g table
unboundedly and all three bus_dmamap_load results are ignored
(failure yields 0 / garbage bus addresses handed to the controller)
What
sys/dev/raid/amr/amr_pci.c:592-603 β amr_sglist_map():
retry:
error = bus_dmamem_alloc(sc->amr_sg_dmat, &p, BUS_DMA_NOWAIT,
&sc->amr_sg_dmamap);
if (error) { ... return(ENOMEM); }
bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, p, segsize,
amr_sglist_helper, &sc->amr_sgbusaddr, 0); /* unchecked */
if (sc->amr_sgbusaddr < 0x2000) {
debug(1, "s/g table too low ...");
goto retry; /* leaks p + the dmamap */
}
- The retry path (documented
XXX HACK ALERT, amr_pci.c:586-591) leaks both the DMA memory and the dmamap of every rejected allocation βsc->amr_sg_dmamapis simply overwritten on the next iteration. The loop has no bound: ifbus_dmamap_loadfails (its return value is ignored, andamr_sglist_helper(amr_pci.c:533-541) also ignores itserrorargument and never writes the address),sc->amr_sgbusaddrstays 0 β0 < 0x2000β retry forever, leakingsegsize(=AMR_NSEG * AMR_MAXCMD * 8bytes β 8β12 KB) plus a tag/map per iteration until kmem/DMA32 exhaustion wedges the machine during attach. - Same unchecked-load pattern in
amr_setup_mbox()(amr_pci.c:654-655) with a worse sink: on load failure the stack variablebaddris uninitialized, yet it flows intosc->amr_mailboxphys = baddr + offsetof(...)(amr_pci.c:660) β the physical address the controller is later told to DMA completions to.amr_ccb_map()(amr_pci.c:698-699) repeats it forsc->amr_ccb_busaddr.
Impact
Hardening / robustness (Info): the leak is certain on any system whose DMA allocator returns a sub-0x2000 address; the load-failure branches are practically unreachable (the memory was just allocated with the same constraints, 1 segment), which is why consequences (unbounded attach-time memory leak; garbage mailbox physical address programmed into the card) are labeled speculative.
Gating
amr hardware required β absent from the audit guest (verified), so
inconclusive.
Fix
Check the bus_dmamap_load return value everywhere, bus_dmamap_unload
+ bus_dmamem_free the rejected block before retrying, and bound the
retry count (see fix.diff).
VERDICT β DF-4708
status: inconclusive (hardware-gated; Info hardening) Β· reproduced: 0 Β· analysis confidence: likely (leak) / speculative (load-failure branches)
Was it reproduced?
No β attach-time code behind amr probe; the audit guest has no
MegaRAID controller (verified: no matching PCI device, /dev/amr*
absent). Static analysis:
- Leak (certain once triggered): every
goto retryiteration (amr_pci.c:600-603) drops the previousbus_dmamem_allocresult and overwritessc->amr_sg_dmamapwithoutbus_dmamap_unload/bus_dmamem_freeβ permanent loss ofsegsizebytes + map (amrvar.h sizing:sizeof(amr_sg[64]entry) * AMR_NSEG * AMR_MAXCMD). - Unbounded loop: if
bus_dmamap_load(amr_pci.c:599) fails, its error is discarded, the helper never setsamr_sgbusaddr(softc, bzeroed β 0),0 < 0x2000holds, and the loop retries forever under persistent failure β unbounded leak until kmem exhaustion during boot/kldload (DoS). amr_setup_mbox(amr_pci.c:654-660): unchecked load; on failure the uninitialized stackbaddrbecomessc->amr_mailboxphysβ the physical address later given to the controller for completion DMA. Practically unreachable (1-segment load of just-allocated, constraint-matched memory) β speculative, but the fix is one line.amr_ccb_map(amr_pci.c:698-699): same unchecked load forsc->amr_ccb_busaddr.
Fix validation
Not possible on this guest. fix_status: not_testable.
Guest state
Guest untouched; stayed up.
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 validation impossible on this guest (no amr hardware to run the attach path); diff authored from the line-accurate trace
fix.diff (sglist retry bound + unload/free + three checked loads)
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (leak trace + loop-unbound proof); README.md; guest gate evidence in ../DF-4705/guest_session.log (no amr PCI device)
PoC changes
no seed; static analysis finding, no runnable trigger (attach-time, hardware-gated)
Verified recommended fix
check bus_dmamap_load returns, free/unload the rejected block before retrying, and bound the retry count (see fix.diff)
Verdict
amr_sglist_map's 'goto retry' loop (amr_pci.c:592-603, the documented sub-0x2000 XXX hack) leaks the rejected s/g table and its dmamap on every iteration and has no retry bound; because bus_dmamap_load's return value is ignored (amr_pci.c:599) and the helper never runs on failure, a persistent load failure leaves amr_sgbusaddr==0 which always satisfies '< 0x2000' -> unbounded leak at attach until kmem exhaustion. amr_setup_mbox (amr_pci.c:654-660) and amr_ccb_map (amr_pci.c:698-699) repeat the unchecked load; in the mailbox case an uninitialized stack baddr would become sc->amr_mailboxphys, the physical completion-DMA address handed to the controller (speculative: 1-segment load of just-allocated constraint-matched memory practically cannot fail). Hardware-gated: no MegaRAID controller on the audit guest, so no runtime confirmation; filed as Info hardening.
No comments yet.