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

bus_dma_tag_create failure returns without cleanup: leaks BAR IRQ and leaves interrupt handler registered

Summary

When bus_dma_tag_create fails at line 320 fwohci_pci_attach returns ENOMEM directly without calling fwohci_pci_detach(self) unlike every other error path in function (lines 282 296 330 all call detach first). Leaks memory BAR resource (sc->bsr) IRQ resource (sc->irq_res) and leaves interrupt handler (sc->ih) registered on partially-initialized softc. device_doattach does NOT call detach on attach failure so cleanup never happens. IRQ handler harmless because fwohci_intr early-returns on intmask==0 but resources permanently consumed preventing re-attach. [VERIFIED pass2: reproduced by counter-interposition - ENOMEM return leaks BAR IRQ and live handler 1/0/1 accounting vs balanced siblings, re-attach refused by rman BAR conflict forever; sharpest edge is the interrupt handler left pointing at the softc that device_doattach frees; fix-validated - missing detach call added, accounting balanced]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2305 Β· 19 files
FileTypeDescriptionSize
fwpci_harness.c β€” 23.5 KB view raw
fwpci_harness.h β€” 1.8 KB view raw
fwohci_harness.c β€” 22.3 KB view raw
fwohci_harness.h β€” 3.9 KB view raw
fwpcictl.c β€” 3.6 KB view raw
fwpci_prep.sh β€” 1.9 KB view raw
build_kernel.sh β€” 358 B view raw
build_fix.sh β€” 519 B view raw
run.sh β€” 117 B view raw
run.log β€” 1.3 KB view raw
dmesg.txt β€” 715 B view raw
run_fixed.log β€” 2.1 KB view raw
fix.diff β€” 896 B view raw
env.txt β€” 1.4 KB view raw
build.log β€” 2.0 KB view raw
README.md β€” 2.7 KB ↓ raw
VERDICT.md β€” 4.5 KB ↓ raw
manifest.json β€” 1.4 KB view raw
verdict.json β€” 3.8 KB view raw

DF-2305 β€” bus_dma_tag_create failure returns without cleanup

Verification pack for finding DF-2305 (sys/bus/firewire/fwohci_pci.c:320-324). Low / base:hw. Claim: when bus_dma_tag_create fails, fwohci_pci_attach returns ENOMEM directly β€” unlike its sibling error paths at :282, :296 and :330, which all call fwohci_pci_detach(self) first β€” leaking the memory BAR resource (sc->bsr), the IRQ resource (sc->irq_res), and leaving the interrupt handler (sc->ih / fwohci_intr) registered.

Verdict (short)

REPRODUCED on the verbatim-interposition harness: with bus_dma_tag_create interposed to fail exactly at its fwohci_pci.c:300 call inside the REAL fwohci_pci_attach, the driver returned ENOMEM having consumed (rig accounting, deltas): MEM 1 alloc / 0 releases, IRQ 1/0, setup_intr 1 / teardown 0 β€” BAR + IRQ leaked and fwohci_intr left registered. A clean re-attach attempt was then refused by the rman-conflict model ("Could not map memory", ENXIO) because the BAR is still outstanding, and the failed re-attach additionally overwrites sc->bsr with NULL, orphaning the BAR permanently. The sibling path (irq-alloc failure at :280, forced through the same rig) allocates and releases in balance (MEM 1/1) because it calls detach. A rig-only forced detach of the leaked state released exactly what the stock error path left behind β€” proving the missing call is the only difference.

Reachability (honest): needs a real fwohci PCI card AND bus_dma_tag_create failing β€” on x86_64 that is the alloc_bounce_pages ENOMEM path (busdma_machdep.c:820-860, active when lowaddr=BUS_SPACE_MAXADDR_32BIT < installed RAM, i.e. any >4GB host, combined with severe memory pressure), or the tag kmalloc (M_INTWAIT, effectively never). Rare but real; hardware-gated. On top of the leak, device_doattach frees the softc after the failed attach (subr_bus.c:2121 device_set_driver -> kfree softc) while the handler stays registered β€” on shared-IRQ hardware that is an interrupt-context use-after-free of the softc pointer.

Files

Same rig as DF-2304 (shared verbatim-interposition harness): * fwpci_harness.c/h, fwohci_harness.c/h, fwpcictl.c β€” rig * run.sh β€” the four-step proof (A leak / C forced-detach / D sibling / B permanence) * run.log, dmesg.txt β€” baseline output + console lines * run_fixed.log β€” fixed-kernel A/B (balanced + re-attach unblocked) * fix.diff β€” combined 3-hunk fix (hunk 2 adds the missing detach)

Reproduce

scp -F dfbsd-qemu/config * dfbsd:/root/fwo/
vm.sh run_root 'sh /root/fwo/fwpci_prep.sh && sh /root/fwo/build_kernel.sh'
vm.sh down && vm.sh up
vm.sh run_root 'sh /root/fwo/run.sh'

Expected: A shows rc=12 with MEM 1/0 IRQ 1/0 setup 1/teardown 0 live_handlers=1; C releases everything; D balanced; B conflict+ENXIO.

VERDICT.md
↓ download raw

DF-2305 VERDICT β€” bus_dma_tag_create failure path leaks BAR, IRQ, handler

  • finding: DF-2305, fwohci_pci.c:320-324 (fwohci_pci_attach)
  • status: reproduced (forced-failure interposition at exactly the fwohci_pci.c:300 bus_dma_tag_create call; resource accounting captured against the real attach/detach code)
  • impact: leak (permanent PCI memory-BAR + IRQ resource leak, interrupt handler left registered on a softc that new-bus frees after the failed attach; device can never attach again)
  • confidence: certain (defect + behavior); trigger probability low (see reachability)

Line-accurate comparison (stock tree)

  • :267-272 MEM BAR alloc fail -> return ENXIO (nothing else held: OK)
  • :278-284 IRQ alloc fail -> fwohci_pci_detach(self) -> return ENXIO
  • :287-298 bus_setup_intr fail -> fwohci_pci_detach(self) -> return ENXIO
  • :300-324 bus_dma_tag_create fail -> return (ENOMEM); with NO detach β€” at this point sc->bsr, sc->irq_res and the registered sc->ih are all held
  • :326-332 fwohci_init fail -> fwohci_pci_detach(self) -> return EIO

The :320 path is the only one that skips cleanup.

Mechanical reproduction (this run)

Rig accounting is built into the fabricated pci bus methods the stock driver dispatches to (bus_alloc_resource / release_resource / setup_intr / teardown_intr counters; a rman-style BAR-conflict model: a still-outstanding BAR refuses the next allocation).

  • A (the bug): fwpcictl attach dmatagfail -> real attach, tag creation fails at :300/:320. Output: rc=12 (ENOMEM), deltas MEM 1/0 IRQ 1/0, setup 1 / teardown 0, live_handlers=1. Console: "fwohci_pci_attach: Could not allocate DMA tag - error 12" followed by NOTHING (no release lines). LEAKED.
  • C (what the missing call would do): rig-only fwohci_pci_detach with the leaked pointers still in the softc -> teardown_intr ok, release_resource(IRQ) ok, release_resource(MEM) ok; counters 1/1 1/1, live=0. The cleanup is exactly one call away.
  • D (sibling comparison): fwpcictl attach irqfail -> irq alloc fails at :280 -> the sibling path runs detach: deltas MEM 1/1, IRQ 0/0, balanced.
  • B (permanence): fresh leak, then a clean re-attach attempt -> bus_alloc_resource(MEM) refused ("BAR 0xf0000000 still outstanding: rman conflict"), "fwohci0: Could not map memory", rc=6/ENXIO; and the failed re-attach assigns sc->bsr = NULL (fwohci_pci.c:267), orphaning the BAR β€” not even a manual detach can recover it afterwards.

Additional impact note (softc lifetime)

On the real failure, new-bus finishes the failed attach with device_set_driver(dev, NULL) (subr_bus.c:2121), which kfree()s the driver softc (subr_bus.c device_set_driver). The still-registered fwohci_intr handler therefore points at freed memory: on shared-IRQ hardware every interrupt on that line dispatches into a freed softc (interrupt-context UAF; mitigated only by fwohci_intr early-returning when the (freed) sc->intmask reads zero). The BAR/IRQ leak alone already permanently prevents re-attach, as demonstrated.

Reachability honesty

Requires (a) real OHCI-1394 PCI hardware (none on this guest; the PCI side is fabricated, the audited code is stock), and (b) a failing bus_dma_tag_create: on x86_64 the tag kmalloc is M_INTWAIT (cannot fail), but with lowaddr=BUS_SPACE_MAXADDR_32BIT on a host with >4GB RAM the tag is marked BUS_DMA_BOUNCE_LOWADDR and, with BUS_DMA_ALLOCNOW, alloc_bounce_zone/alloc_bounce_pages can return ENOMEM under real memory pressure (sys/platform/pc64/x86_64/busdma_machdep.c:820-860) β€” i.e. exactly the machines this driver targets. Probability per boot is low; the code defect and its consequence are deterministic.

Fix validation (A/B)

fix.diff hunk 2 adds fwohci_pci_detach(self); before the ENOMEM return (mirroring the sibling paths). Fixed kernel (run_fixed.log): * attach dmatagfail -> rc=12 still, but deltas MEM 1/1 IRQ 1/1, teardown 1, live_handlers=0 β€” balanced. * immediate re-attach probe (attach irqfail): MEM 1/1 with ZERO conflicts β€” the BAR was released, re-attach is unblocked (reaches the irq step, "Could not allocate irq", sibling path handles it). Baseline vs fixed: leak + permanent conflict vs balanced + unblocked.

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

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log: test A deltas (MEM 1/0 IRQ 1/0 setup 1 teardown 0 live_handlers=1, rc=12), test C forced-detach releases all, test D sibling balanced MEM 1/1, test B re-attach conflict rc=6. dmesg.txt: stock console lines (Could not allocate DMA tag - error 12 with no cleanup following; Could not allocate irq then MEM released on the sibling path; Could not map memory on the conflict path). run_fixed.log: fixed kernel balanced (1/1, 1/1, teardown 1, live 0) and re-attach unblocked (zero conflicts).

PoC changes

Built on the new fwpci_harness.c rig (shared with DF-2304): counter-based fail-exactly-at-Nth interposition of bus_dma_tag_create; accounting built into the fabricated pci bus methods; rman-style BAR-conflict model to demonstrate permanence; rig softc preserves leaked resource pointers across runs so a forced detach can prove what the missing cleanup would release.

Verified recommended fix

Call fwohci_pci_detach(self) before the ENOMEM return at fwohci_pci.c:323, mirroring the sibling error paths at :282/:296/:330.

Verdict

Forced-failure interposition of bus_dma_tag_create exactly at its fwohci_pci.c:300 call inside the REAL fwohci_pci_attach (verbatim TU rig) returned ENOMEM at fwohci_pci.c:320-324 with NO cleanup: rig resource accounting captured MEM 1 alloc / 0 releases, IRQ 1/0, setup_intr 1 / teardown 0, live_handlers=1 (BAR + IRQ leaked, fwohci_intr still registered), console showed the stock Could-not-allocate-DMA-tag kprintf followed by no release activity. A clean re-attach was then refused by the rman BAR-conflict model (Could not map memory, ENXIO) and the failed re-attach overwrote sc->bsr with NULL, orphaning the BAR permanently. The sibling error path (irq alloc failure at :280) run through the same rig was balanced (MEM 1/1) because it calls fwohci_pci_detach; a rig-only forced detach of the leaked state released exactly what the stock path left behind. Additional finding during verification: after the failed attach, device_doattach calls device_set_driver(dev, NULL) which kfrees the softc (subr_bus.c:2121) while the handler stays registered, so on real shared-IRQ hardware the leaked handler dispatches into freed memory. Reachability is hardware-gated (needs an OHCI-1394 card) plus a genuine bus_dma_tag_create ENOMEM (alloc_bounce_pages path on >4GB hosts under memory pressure; the tag kmalloc is M_INTWAIT), so rare in practice but deterministic once it fires. Low severity confirmed; fixed by hunk 2 of fix.diff (adds the missing detach), A/B validated: balanced accounting and re-attach unblocked on the fixed kernel.