β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4708

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4708 Β· 4 files
FileTypeDescriptionSize
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 */
    }
  1. 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_dmamap is simply overwritten on the next iteration. The loop has no bound: if bus_dmamap_load fails (its return value is ignored, and amr_sglist_helper (amr_pci.c:533-541) also ignores its error argument and never writes the address), sc->amr_sgbusaddr stays 0 β†’ 0 < 0x2000 β†’ retry forever, leaking segsize (= AMR_NSEG * AMR_MAXCMD * 8 bytes β‰ˆ 8–12 KB) plus a tag/map per iteration until kmem/DMA32 exhaustion wedges the machine during attach.
  2. Same unchecked-load pattern in amr_setup_mbox() (amr_pci.c:654-655) with a worse sink: on load failure the stack variable baddr is uninitialized, yet it flows into sc->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 for sc->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.md
↓ download raw

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 retry iteration (amr_pci.c:600-603) drops the previous bus_dmamem_alloc result and overwrites sc->amr_sg_dmamap without bus_dmamap_unload / bus_dmamem_free β€” permanent loss of segsize bytes + 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 sets amr_sgbusaddr (softc, bzeroed β†’ 0), 0 < 0x2000 holds, 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 stack baddr becomes sc->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 for sc->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_testable
baseline no→ patch + rebuild →patched clean

fix 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)
↓ fix.diffper-fix-DF-4708

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.