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

dpt_pci_attach() leaks parent_dmat on the dpt_init() failure path β€” dpt_free()'s init_level ladder never destroys the DMA tag the PCI glue created

Summary

Split ownership: the glue creates parent_dmat, common-code dpt_free unwinds only dpt_init state, nobody owns the destroy (rg-exhaustive) - one tag leaks per failed attach. One-line destroy-after-free fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5154 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.5 KB ↓ raw
VERDICT.md β€” 3.2 KB ↓ raw
fix.diff β€” 427 B view raw
verdict.json β€” 2.9 KB view raw
env.txt β€” 992 B view raw

DF-5154 β€” dpt_pci_attach() leaks parent_dmat on the dpt_init() failure path (dpt_free() never destroys the glue's DMA tag)

File: sys/dev/raid/dpt/dpt_pci.c:134-156 Class: resource leak on error path (CWE-401) Severity: Info (one-shot, hardware-gated, boot/kldload-time only) Distinct from DF-2151 (known, not re-reported): DF-2151 is the wrong-rid bus_release_resource; this is the DMA-tag lifecycle gap. Also distinct from DF-5153 (the bus_setup_intr-failure unwind): that path additionally leaks parent_dmat too, but this finding is the one that fires even on the earlier, otherwise-correct dpt_init failure path where dpt_free is called.

The bug

The PCI glue creates the parent DMA tag (dpt_pci.c:134-143):

    if (bus_dma_tag_create(/*parent*/NULL, ..., &dpt->parent_dmat) != 0) {
        dpt_free(dpt);
        error = ENXIO;
        goto bad;
    }
    ...
    if (dpt_init(dpt) != 0) {          /* dpt_pci.c:151 */
        dpt_free(dpt);             /* <-- frees children only */
        error = ENXIO;
        crit_exit();
        goto bad;
    }

dpt_free (dpt_scsi.c:1172-1204) unwinds exactly the objects dpt_init created, stepping init_level 5β†’0: dccb_dmamap unload/free/destroy, dccb_dmat, buffer_dmat, the sg-map list and sg_dmat. It never touches parent_dmat β€” the tag the glue created before dpt_init ran. rg parent_dmat sys/dev/raid/dpt/ shows the full picture: created once (dpt_pci.c:143), consumed as the parent of the three child tags (dpt_scsi.c:1235/1356/1370), destroyed nowhere. On the dpt_init failure path the tag (and its reference on the bus DMA machinery) leaks permanently. The success path never destroys it either β€” but there the driver intentionally keeps it for the device lifetime (there is no detach method at all), so only the error path is a defect.

Impact

Bounded: one bus_dma_tag_t + policy copy leaked per failed attach of a DPT 0x1044:0xa400 controller. Requires the real HBA present and dpt_init failing (board config handshake failure, DMA tag creation failure inside dpt_init, CCB allocation failure). No unprivileged trigger; no memory-safety consequence; correctness/hygiene only. Filed as hardening completeness so the family's teardown story is whole (mlx/ips/ida/amr/mfi siblings each got their unwind findings this pass).

Fix

fix.diff: destroy parent_dmat after dpt_free(dpt) on the dpt_init-failure path (child tags are destroyed by dpt_free, so the parent destroy afterwards is legal). DF-5153's fix performs the same destroy on the later bus_setup_intr-failure path.

VERDICT.md
↓ download raw

DF-5154 VERDICT β€” parent_dmat leak on dpt_init failure

Status: untested (hardware-gated β€” no DPT controller on the audit guest, pciconf -l shows no 0x1044 device; see env.txt). Impact: none demonstrable on the guest β€” the defect itself is line-proven (confidence: certain), the impact is a bounded boot-time resource leak on real hardware only. Fix: authored (fix.diff, applies clean via git apply --check); fix validation not_testable (baseline cannot be reproduced without the HBA).

1. Root cause (line-certain)

  • sys/dev/raid/dpt/dpt_pci.c:134-143 β€” glue creates dpt->parent_dmat (parent NULL, i.e. rooted at the bus's own tag).
  • sys/dev/raid/dpt/dpt_pci.c:151-156 β€” on dpt_init failure the glue calls dpt_free(dpt) and returns; parent_dmat is never destroyed.
  • sys/dev/raid/dpt/dpt_scsi.c:1172-1204 β€” dpt_free teardown ladder (init_level 5β†’0) destroys dccb_dmamap/dccb_dmat/buffer_dmat/ sg-maps/sg_dmat β€” all objects dpt_init created. parent_dmat was created by the glue before dpt_init, and dpt_free is common code shared with no other glue in this tree (dpt_pci.c is the only caller of dpt_alloc/dpt_free in sys/dev/raid/dpt/), so the omission is simply a split-responsibility bug: nobody owns the destroy.
  • Exhaustive reference check: rg -n parent_dmat sys/dev/raid/dpt/ β†’ creation (dpt_pci.c:143), three child-tag uses (dpt_scsi.c:1235, :1356, :1370), softc declaration (dpt.h:986). No bus_dma_tag_destroy of it anywhere.

2. Reachability / threat model

Requires a DPT SmartRAID PCI HBA (probe match at dpt_pci.c:61-66) and a dpt_init failure (config handshake via dpt_get_conf, DMA tag/CCB/sg-map allocation failures β€” several are device-fed, cf. DF-4245). Both conditions are boot/kldload-time; no unprivileged trigger, no user-controllable input, no kernel-memory consequence β€” the leak is a bus_dma_tag_t plus policy state per failed attach. Severity Info; would be Low only if a repeat-attach loop were possible (newbus does not retry failed attaches).

3. Why untested

Same hardware gating as DF-5153: the guest has no DPT device, so the path cannot be entered even by a KLD harness without fabricating the entire HBA config handshake (which would demonstrate nothing beyond the code text β€” the leak is directly readable from the source: create-without-destroy). Family precedent: DF-5115 (untested, analysis-verified).

4. Fix

fix.diff β€” bus_dma_tag_destroy(dpt->parent_dmat) after dpt_free(dpt) on the dpt_init-failure path; ordering is legal because dpt_free has already destroyed the three child tags. git apply --check clean against the audited tree.

5. Kernel references

sys/dev/raid/dpt/dpt_pci.c:134-143, :151-156; sys/dev/raid/dpt/dpt_scsi.c:1172-1204, :1235, :1356, :1370; sys/dev/raid/dpt/dpt.h:986.

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.diff authored post-verification; git apply --check clean (single hunk, dpt_pci.c:150-156). Baseline not reproducible on the guest (no DPT HBA) so patched-kernel validation is not_testable.

findings/poc/DF-5154/fix.diff; VERDICT.md section 4.
↓ fix.diffper-fix-DF-5154

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md (create-without-destroy trace), VERDICT.md section 1 (exhaustive rg reference check), fix.diff (git-apply-clean one-hunk destroy after dpt_free), env.txt. Decisive lines: sys/dev/raid/dpt/dpt_pci.c:143 vs dpt_scsi.c:1174-1202 switch ladder with no parent_dmat case.

PoC changes

no seed PoC existed; none authored - path unreachable without DPT hardware; the leak is directly readable from source (create without destroy), a harness would add nothing

Verified recommended fix

bus_dma_tag_destroy(dpt->parent_dmat) immediately after dpt_free(dpt) on the dpt_init-failure path (child tags already destroyed by dpt_free, so the ordering is legal).

Verdict

Line-proven DMA-tag leak: dpt_pci_attach creates dpt->parent_dmat (dpt_pci.c:134-143) before dpt_init, but dpt_free's init_level teardown ladder (dpt_scsi.c:1172-1204) destroys only the objects dpt_init created (dccb/sg/buffer tags, sg maps, CCB DMA memory) - parent_dmat is destroyed nowhere in the driver (rg: created once at dpt_pci.c:143, consumed at dpt_scsi.c:1235/1356/1370, no bus_dma_tag_destroy call). On the dpt_init failure path (dpt_pci.c:151-156) the tag leaks permanently per failed attach. Untested: hardware-gated (guest pciconf has no 0x1044:0xa400 device, env.txt), no unprivileged trigger, no memory-safety consequence - bounded boot-time resource leak only. Info hardening completeness for the raid-family teardown story.