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)
PoC verification
Evidence pack
findings/poc/DF-2305 Β· 19 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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.
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_detachwith 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_testableConfirmed 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.
No comments yet.