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

bt_pci_attach() error unwind after bus_setup_intr() failure leaves an XPT-registered SIM and wildcard CAM path live over released port/IRQ rman records, freed DMA memory and a lockuninit'd softc lock (UAF-class teardown)

Summary

bt_attach registers the CAM bus/path BEFORE setup_intr but its failure branch skips the CAM teardown the two earlier branches perform - the glue then releases everything, leaving the sim's closures pointing at the freed softc: the CAM scan runs on a destroyed lock through released ports into freed DMA memory. The dpt DF-5153 sibling shape. Hardware-gated boot-time trigger. Mirror-the-earlier-branch teardown fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5167 Β· 4 files
FileTypeDescriptionSize
README.md β€” 3.1 KB ↓ raw
VERDICT.md β€” 4.7 KB ↓ raw
verdict.json β€” 3.4 KB view raw
fix.diff β€” 726 B view raw

DF-5167 β€” bt: bus_setup_intr() failure in bt_attach leaves XPT-registered SIM and

wildcard path live against a softc whose resources bt_pci_attach() then releases

File: sys/dev/disk/buslogic/bt_pci.c (with root cause in sys/dev/disk/buslogic/bt.c) Pass 2 (GLM 5.3). Severity: Low. Confidence: certain (static code-path proof).

What breaks

bt_attach() (bt.c:737) registers a CAM SIM and wildcard path before it sets up the interrupt:

  • bt.c:770 xpt_bus_register(bt->sim, 0) β€” succeeds
  • bt.c:776 xpt_create_path(&bt->path, ... CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) β€” succeeds
  • bt.c:789 bus_setup_intr(dev, bt->irq, INTR_MPSAFE, bt_intr, bt, &bt->ih, NULL) β€” on failure prints and returns the error at bt.c:792-794 without xpt_bus_deregister(), xpt_free_path() or cam_sim_free().

The PCI glue then treats any bt_attach() failure as "nothing was published" and releases everything (bt_pci.c:152-156):

  • bt_pci.c:93-96 bus_release_resource(dev, SYS_RES_IOPORT, BT_PCI_IOADDR, bt->port)
  • bt_pci.c:97-98 bus_release_resource(dev, SYS_RES_IRQ, 0, bt->irq)
  • bt_pci.c:99 β†’ bt.c:179-236 bt_free_softc(): unloads/frees the mailbox block, the CCB array and every S/G map, destroys all child DMA tags, and lockuninit(&bt->lock) (bt.c:236).

Result: the XPT still owns a registered bt bus whose btaction/btpoll closures capture the softc. The CAM probe periph reacts to the bus registration and issues a bus scan; btaction() then:

  1. runs under bt->lock β€” a lock that has just been lockuninit()'d;
  2. touches bt_inb(bt, STATUS_REG) through an rman port record that was already released (and can be re-allocated to a different device on the same ports);
  3. pulls BCCBs from bt->free_bt_ccbs, which points into bus_dmamem_free()'d memory β€” a use-after-free read/write path once the pages are reused.

This is the BusLogic sibling of dpt_pci DF-5153 (same family, same pass) and is distinct from the known bt_pci.c pair DF-2162/DF-2431 (which cover only the parent_dmat tag leak; this finding is about CAM state + released rman/DMA memory, a different resource set and failure point).

Trigger / threat

Boot-time only: a real BusLogic MultiMaster PCI HBA must be present and bus_setup_intr() must fail (interrupt table exhaustion, conflicting MP/ACPI table entries, ...). Not reachable from an unprivileged user; not controllable by a hostile card (the failure is host-side). Impact ceiling: panic / kernel memory corruption during/after boot on that machine. Hence Low.

Verification status

Skipped per contract (Low severity, hardware-preconditioned trigger β€” the QEMU guest has no BusLogic PCI HBA, so neither the boot-time failure nor the card itself can be instantiated). verdict.json carries the persistable status untested for that reason; the finding is proven by code-path analysis with exact citations (see VERDICT.md).

Fix

fix.diff β€” deregister/free the CAM state in bus_setup_intr()'s error branch in bt.c before returning, mirroring the existing xpt_create_path failure branch (bt.c:776-783).

VERDICT.md
↓ download raw

DF-5167 β€” VERDICT

Status: skipped / untested (static proof only). Reproduced: no (not reachable on the audit guest). Classification if triggered: kernel use-after-free class teardown defect, boot-time availability/integrity.

Narrative

The code path, proven line by line

  1. bt_pci_attach() (sys/dev/disk/buslogic/bt_pci.c:118) allocates port + IRQ resources (bt_pci_alloc_resources, bt_pci.c:51-86; IRQ at bt_pci.c:76-77 with RF_ACTIVE | RF_SHAREABLE) and calls bt_init_softc() (bt_pci.c:83 β†’ bt.c:163), which stores bt->port, bt->irq and lockinit(&bt->lock) (bt.c:175).
  2. bt_pci_attach() then runs bt_probe() || bt_fetch_adapter_info() || bt_init() (bt_pci.c:147). bt_init() (bt.c:580-735) raises bt->init_level to 8, allocating the mailbox block, the CCB array, S/G maps and five child DMA tags.
  3. bt_attach() (bt.c:737) then: - bt.c:770: xpt_bus_register(bt->sim, 0) β€” the XPT now owns the bus; - bt.c:776: xpt_create_path(&bt->path, NULL, ..., CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); - bt.c:789-794: bus_setup_intr() β€” on failure: device_printf + return (error) with NO xpt_bus_deregister(), NO xpt_free_path(), NO cam_sim_free(). (Contrast the two earlier failure branches at bt.c:770-774 and 776-783, which deregister and free the sim β€” the last branch simply forgot.)
  4. bt_pci_attach() sees the error (bt_pci.c:152-156) and calls bt_pci_release_resources() (bt_pci.c:88-100): - releases the IOPORT rman record (bt_pci.c:95-96), - releases the IRQ rman record (bt_pci.c:97-98), - bt_free_softc() (bt.c:179-236): unload/free mailbox + CCB array + all S/G maps, destroy all child DMA tags, lockuninit(&bt->lock) (bt.c:236).

Why the leftover state is dangerous

The registered SIM's btaction/btpoll closures capture bt (softc pointer, bt.c:764-765 cam_sim_alloc(btaction, btpoll, "bt", bt, ...)). After step 4 the CAM layer (probe periph reacting to the bus registration; any later XPT request on the wildcard path bt->path) can still invoke btaction(), which:

  • is entered under the sim lock &bt->lock (a destroyed lock object),
  • reads card registers through the released port resource (on re-allocation of those ports to another driver this becomes cross-device register access),
  • executes btgetccb() (bt.c:870) popping bt->free_bt_ccbs β€” a list embedded in bus_dmamem_free()'d memory (bt.c:192-232 case 6/8 of bt_free_softc), and writes CCB state there β†’ use-after-free read/write.

The init_level ladder in bt_free_softc (bt.c:183-236) pairs every init_level++ in bt_init (bt.c:625,642,650,659,682,690,698,714 β€” max level 8; cases 9-11 for sense buffers are dead code in this driver), so the DMA unwind itself is complete except parent_dmat β€” that omission is the known DF-2162/DF-2431 pair and is intentionally not re-reported here. This finding is the other half of the unwind that is broken: the CAM registration done by bt_attach().

Why it was not reproduced on the guest

  • Requires a physical BusLogic MultiMaster PCI HBA (VID 0x104B, DID 0x1040/0x0140, bt_pci.c:47-48,105-107) present in the machine.
  • Requires the host-side bus_setup_intr() to fail at boot β€” not attacker- or card-controllable.
  • The QEMU audit guest has no such device; the failure point cannot be reached by any local program. Per the audit contract, Low/hardware-preconditioned findings skip the QEMU phase.

Family calibration (same pass)

  • dpt_pci DF-5153 (Low, CWE-459): identical shape, additionally left a shutdown handler and global softc list entry live.
  • adv_pci DF-5043 (Low): the "IRQ resource left allocated" variant β€” not applicable to bt, whose glue does release the IRQ (bt_pci.c:97-98).
  • adv_pci DF-5042 / dpt_pci DF-5154 = the parent_dmat leak family = bt's known DF-2162/DF-2431 (not re-reported).

Bottom line

Certain-by-inspection boot-time teardown defect: the last error branch of bt_attach() leaks the CAM registration, and the PCI glue's unconditional resource release then pulls the port/IRQ records, the DMA memory and the softc lock out from under the still-registered SIM β€” a UAF-class hazard bounded by the rare trigger (vintage PCI HBA + IRQ-setup failure at boot), so Low severity, no userland attack path, not testable on the audit guest.

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 statically against the read-only sys/ tree after line-accurate confirmation of the root cause; not built or run because the trigger hardware does not exist on the audit guest (verification was skipped for the same reason).

findings/poc/DF-5167/fix.diff (git-apply-able unified diff vs sys/dev/disk/buslogic/bt.c; never applied to sys/)
↓ fix.diffper-fix-DF-5167

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-5167/VERDICT.md (full line-cited proof); bt_pci.c:152-156 -> bt_pci.c:88-100 -> bt.c:179-236 (release side); bt.c:770-794 (the branch that leaks the CAM registration); README.md for the family calibration

PoC changes

No PoC exists: the trigger requires a physical BusLogic MultiMaster PCI HBA (VID 0x104B DID 0x1040/0x0140, bt_pci.c:47-48) plus a boot-time bus_setup_intr() failure; neither can be instantiated on the QEMU guest. Static-analysis evidence pack only.

Verified recommended fix

In bt_attach()'s bus_setup_intr() error branch, xpt_free_path + xpt_bus_deregister + cam_sim_free under bt->lock before returning (see fix.diff), mirroring the existing xpt_create_path failure branch at bt.c:776-783.

Verdict

Skipped per contract (Low severity, hardware-preconditioned boot-time trigger): proving it by code inspection only. bt_attach()'s bus_setup_intr() failure branch (bt.c:789-794) returns without deregistering the XPT bus registered at bt.c:770 or freeing the wildcard path/sim created at bt.c:776; bt_pci_attach() (bt_pci.c:152-156) then calls bt_pci_release_resources(), which releases the port and IRQ rman records (bt_pci.c:93-98) and bt_free_softc() unloads/frees the mailbox block, CCB array, S/G maps and lockuninit's bt->lock (bt.c:179-236). The still-registered SIM's btaction/btpoll closures capture the softc, so the CAM probe periph's bus scan reaches a destroyed lock, released port resources and bt->free_bt_ccbs living in bus_dmamem_free()'d memory - a UAF-class teardown hazard, reachable only at boot with a real BusLogic PCI HBA whose interrupt setup fails. Not reachable on the QEMU audit guest (no such PCI device, not user-controllable). Distinct from known DF-2162/DF-2431 (parent_dmat tag leak only); sibling of dpt_pci DF-5153.