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

trm_detach frees I/O port, SRB array and DMA tags before bus_teardown_intr and SIM deregistration, with no drain of in-flight CCBs β€” use-after-free / permanent CAM wedge on kldunload

Summary

trm_detach releases pACB->iores first, frees SG maps of only the free-list SRBs, frees the entire 256-entry SRB array and all dma tags BEFORE bus_teardown_intr and BEFORE xpt_bus_deregister/cam_sim_free. In-flight CCBs are never xpt_done'd; their ccb_trmsrb_ptr dangles into freed memory; the live interrupt handler can execute against released bus space; CAM can still call trm_action on the freed softc. Root kldunload trm.ko or physical PCI hot-remove while I/O is in flight -> ISR/CAM touch freed SRB array / released MMIO -> UAF panic or leaked CCBs wedging the SIM queue until reboot. Reorder+drain fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3830 Β· 6 files
FileTypeDescriptionSize
README.md β€” 2.5 KB ↓ raw
VERDICT.md β€” 3.0 KB ↓ raw
verdict.json β€” 2.9 KB view raw
fix.diff β€” 3.5 KB view raw
env.txt β€” 1.1 KB view raw
manifest.json β€” 742 B view raw

DF-3830 β€” trm_detach frees live DMA/bus resources before tearing down the interrupt and deregistering the SIM

  • File: sys/dev/disk/trm/trm.c (pass 2)
  • Class: CWE-99 / CWE-416 (use-after-free via improper shutdown ordering), missing queue drain
  • Severity: Low (trigger requires root kldunload trm.ko β€” module not in GENERIC β€” or physical PCI hot-removal of a Tekram DC395U/U2 adapter while I/O is in flight)

Build / Run / Expected

Not runnable on the audit guest: no Tekram PCI device (see env.txt); trm_probe() only matches 0x03911DE1/0x03921DE1, so the driver never attaches and trm_detach is unreachable there. On hardware, the trigger is:

kldload trm.ko            # card present, disk busy (dd if=/dev/daX of=/dev/null bs=1m)
kldunload trm.ko          # trm_detach runs with SRBs in flight

Expected on vulnerable code: interrupt storm / kernel panic (NULL or freed pACB->pFreeSRB, pSRB->pccb), or leaked CCBs wedging the CAM queue β€” any of which constitutes the UAF.

Root cause (order of operations in trm_detach, trm.c:3692-3716)

  1. bus_release_resource(dev, SYS_RES_IOPORT, ...) β€” trm.c:3696 β€” I/O port region RELEASED FIRST, while pACB->irq is still installed. Any interrupt between here and bus_teardown_intr (trm.c:3710) makes trm_Interrupt do bus_space_read/write on a released region.
  2. trm_destroySRB(pACB) β€” trm.c:3697 β€” frees SG lists and pSRB->dmamap for only the SRBs on pACB->pFreeSRB (trm.c:2999-3015). SRBs sitting on per-target pWaitingSRB/pGoingSRB lists and the active pActiveSRB are NOT visited: their maps leak and β€” worse β€”
  3. bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB, ...) β€” trm.c:3700 β€” frees the entire 256-entry SRB array that those in-flight CCBs still reference (ccb_h.ccb_trmsrb_ptr), while their pccbs are never xpt_done'd (no drain, no freeze).
  4. Only afterwards: bus_teardown_intr (3710), then xpt_bus_deregister/cam_sim_free (3714-3715). Until then CAM still owns a registered SIM whose trm_action can be invoked concurrently against freed softc state (pACB->pFreeSRB now dangling, dma tags destroyed at 3698/3703/3708/3709 before the SIM that uses them is gone).

Contrast with trm_attach's own error path (trm.c:3631-3665) which at least tears down in the sensible order (no interrupt/DMA race there because the SIM was never registered on failure).

Fix

Reorder trm_detach: deregister the SIM first (after flushing), tear down the interrupt second, release DMA memory third, I/O port last. See fix.diff.

VERDICT.md
↓ download raw

VERDICT β€” DF-3830

status: inconclusive (source-certain, hardware-gated) β€” reproduced: 0 β€” impact demonstrated on guest: none (confirmed impact class on hardware: UAF/panic/wedge; not exercised β€” no Tekram HBA on the audit guest, env.txt).

Why this is a real defect (line-cited trace)

trm_detach (sys/dev/disk/trm/trm.c:3692-3716) executes, in this order:

step line operation consequence
1 3696 bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), pACB->iores) chip registers unmapped while IRQ still live (teardown at 3710) β†’ ISR (trm_Interrupt, trm.c:1300-1352) touches a released bus space
2 3697 trm_destroySRB(pACB) walks only pACB->pFreeSRB (trm.c:3003-3014) β€” SRBs on pDCB->pWaitingSRB, pDCB->pGoingSRB, pDCB->pActiveSRB keep live sg_dmamap/dmamap
3 3698-3709 destroy sg_dmat, free the whole SRB array (bus_dmamem_free(srb_dmat, pACB->pFreeSRB, …), 3700), free sense buffers, destroy buffer_dmat every in-flight CCB's ccb_h.ccb_trmsrb_ptr (set at trm.c:588) now dangles; their pccbs are never xpt_done'd (no drain anywhere) β†’ CAM wedge + UAF on next touch
4 3710 bus_teardown_intr(dev, pACB->irq, pACB->ih) the ISR could still be running against steps 1-3 state
5 3712-3715 xpt_async(AC_LOST_DEVICE), xpt_free_path, xpt_bus_deregister, cam_sim_free the SIM that calls trm_action/trm_poll into the freed softc is only deregistered last

There is no xpt_freeze_simq, no drain of going/waiting SRBs, no completion of outstanding CCBs β€” compare FreeBSD's later trm(4), and the driver's own attach-failure path which never has the SIM registered while freeing (trm.c:3631-3665).

Reachability

  • trm is not in X86_64_GENERIC; it is a KLD (sys/dev/disk/trm/Makefile). Trigger: root kldunload trm.ko with I/O in flight, or surprise hot-removal of the PCI card.
  • Root-only / physical β†’ severity Low, but the primitive is a straight UAF of the 128 KiB SRB array (TRM_MAX_SRB_CNT * sizeof(TRM_SRB), trm.c:3441-3456) plus unmapped-MMIO ISR execution.
  • Guest: pciconf -l shows 7 devices, none with vendor 0x1DE1 (env.txt) β†’ trm_probe (trm.c:3677-3688) returns ENXIO, driver never attaches, trm_detach unreachable. Hence inconclusive rather than reproduced.

Fix validation

fix.diff reorders detach into: flush+deregister SIM β†’ teardown IRQ β†’ free SRB DMA β†’ free sense β†’ destroy tags β†’ release I/O port, and drains going/waiting lists (complete with CAM_UNEXP_BUSFREE, recycle SRB) before freeing the array. Not exercisable on this guest (no hardware); compiles-by-inspection against the current tree only.

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

Baseline and patched behavior both require an attached Tekram DC395 adapter; absent on the audit guest. fix.diff is git-apply-able against sys/dev/disk/trm/trm.c (verified) and compiles by inspection.

fix.diff
↓ fix.diffper-fix-DF-3830

Confirmed kernel references

Detail

Exploit chain

root-only/physical: kldunload trm.ko (module not in GENERIC) or surprise PCI removal while I/O is in flight -> ISR/CAM touch freed SRB array and released bus space -> UAF panic or leaked CCBs wedging the SIM queue. No unprivileged route.

Evidence (decisive lines)

VERDICT.md (order-of-operations table with line refs); env.txt (guest hardware absence); fix.diff (reordered detach + trm_detach_flush drain, git-apply --check passes)

PoC changes

No runnable PoC exists without the PCI card; pack is analysis + fix only.

Verified recommended fix

Reorder trm_detach: freeze+deregister SIM, drain/complete in-flight CCBs, bus_teardown_intr, then free DMA memory, release IOPORT last (fix.diff).

Verdict

trm_detach (sys/dev/disk/trm/trm.c:3692-3716) releases the chip I/O port FIRST (3696), frees SG maps for only the free-list SRBs (trm_destroySRB, 3697 -> 2999-3015), then frees the entire 256-entry SRB array with bus_dmamem_free (3700) and destroys every dma tag (3698-3709) BEFORE bus_teardown_intr (3710) and BEFORE the SIM is deregistered (xpt_bus_deregister/cam_sim_free, 3714-3715), with no xpt_freeze_simq and no drain: in-flight CCBs are never xpt_done'd, their ccb_h.ccb_trmsrb_ptr dangles into freed memory, the still-registered interrupt can fire into released bus space, and CAM can still call trm_action against the freed softc. kldunload/hot-remove therefore yields a use-after-free and/or permanent CAM wedge. Source-certain; not exercisable on the audit guest because no Tekram PCI device exists (env.txt: pciconf shows 7 QEMU devices, none vendor 0x1DE1), so trm never attaches and detach is unreachable there.