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

dpt_pci_attach() bus_setup_intr failure leaves XPT-registered SIMs, CAM wildcard paths, shutdown_post_sync handler and softc (on global dpt_softcs) live against released io/irq β€” phantom CAM bus issues bus_space I/O through a released rman record and never completes a command

Summary

The setup_intr-failure goto bad: undoes nothing dpt_attach registered - the phantom SIM stays XPT-resolvable forever, dpt_action pokes registers through the released window, no completion is ever reaped (boot scan hangs), and dptshutdown keeps firing each reboot. Hardware-gated boot-time wedge; full reverse-order teardown fix (3-file, apply-clean).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5153 Β· 5 files
FileTypeDescriptionSize
README.md β€” 4.8 KB ↓ raw
VERDICT.md β€” 4.7 KB ↓ raw
fix.diff β€” 3.1 KB view raw
verdict.json β€” 3.6 KB view raw
env.txt β€” 992 B view raw

DF-5153 β€” dpt_pci_attach() bus_setup_intr failure leaves XPT-registered SIMs, CAM paths, shutdown handler and softc live against released io/irq

File: sys/dev/raid/dpt/dpt_pci.c:163-179 (trigger), :159 (setup) Class: incomplete error-path unwind β†’ logical UAF of bus resources + permanent leak + unkillable CAM I/O (CWE-459 Incomplete Cleanup) Severity: Low (hardware-gated: needs a DPT 0x1044:0xa400 HBA and an interrupt-registration failure at attach) Distinct from DF-2151 (known, not re-reported): DF-2151 is the wrong-rid bus_release_resource on the bad: path. This finding is everything else that path fails to unwind β€” the CAM/XPT lifetime, the shutdown handler, the DMA machinery and the softc. (Note the IRQ release at dpt_pci.c:176-177 uses rid 0, which is the rid the IRQ was allocated with at :103-105 β€” correct; only the io rid is wrong, and that half is DF-2151.)

The bug

dpt_pci_attach reaches bus_setup_intr only after the controller is fully brought online:

    crit_enter();
    if (dpt_init(dpt) != 0) { ... }          /* DMA tags, CCB array, sg maps */
    /* Register with the XPT */
    dpt_attach(dpt);                          /* dpt_pci.c:159 */
    crit_exit();

    error = bus_setup_intr(dev, irq, 0, dpt_intr, dpt, &ih, NULL);
    if (error) {
        device_printf(dev, "Unable to register interrupt handler\n");
        error = ENXIO;
        goto bad;                          /* dpt_pci.c:168 */
    }
    ...
bad:
    if (io)
        bus_release_resource(dev, iotype, 0, io);   /* wrong rid = DF-2151 */
    if (irq)
        bus_release_resource(dev, SYS_RES_IRQ, 0, irq);  /* correct rid */
    return (error);

By the time bus_setup_intr runs, dpt_attach (dpt_scsi.c:1450-1490) has:

  • cam_sim_alloc'd up to MAX_CHANNELS SIMs sharing one devq and xpt_bus_register'd each (dpt_scsi.c:1466-1473),
  • xpt_create_path'd wildcard paths for each (dpt_scsi.c:1475-1482),
  • released the shared devq (dpt_scsi.c:1485),
  • EVENTHANDLER_REGISTER'd shutdown_post_sync β†’ dptshutdown, discarding the tag (dpt_scsi.c:1487-1488),

and dpt_alloc (dpt_scsi.c:1153-1169) has TAILQ-link'd the softc onto the global dpt_softcs list (:1165). None of this is undone on the failure path: bad: touches only io/irq; dpt_free is never called; no bus is deregistered; no sim/path freed; the shutdown handler stays registered; the softc stays on dpt_softcs; the DMA'able CCB array, sg maps and three DMA tags stay allocated forever.

Consequences (why it is not merely cosmetic)

  1. Phantom SIM in CAM. xpt_bus_register is global state independent of newbus attach status. After the failed attach the bus/sim/paths remain fully resolvable; CAM's own boot-time scan (and anything else that compiles a path to that bus) reaches dpt_action β†’ dptexecuteccb, which pokes dpt_outb() through dpt->tag/bsh β€” a bus_space handle to an rman record that was just released (or, with DF-2151's wrong-rid no-op, leaked). Cross-device register corruption on x86 PIO; MMIO variant touches the re-mapped page owner.
  2. Unkillable I/O. No interrupt handler was installed, so nothing the HBA completes is ever reaped β€” every command routed to the phantom bus hangs in CAM forever (boot-time wedging of the SCSI scan path).
  3. Shutdown-time use of released state. dptshutdown (dpt_scsi.c:1717-1733) still fires on every reboot and issues dpt_send_immediate through the released/leaked register window.
  4. Permanent leak of the CCB DMA memory, sg maps, three child DMA tags, io (via the DF-2151 no-op) β€” and see DF-5154 for parent_dmat, which is additionally never destroyed even where dpt_free is called.

No kernel-heap corruption primitive is in scope here (bus-space logical UAF plus leak), and the trigger is hardware/boot-time β€” there is no unprivileged trigger and no uid=0 route; the class does not permit one.

Reproduction status

untested β€” hardware-gated. Verified on the audit guest (pciconf -l β†’ no vendor 0x1044 device; see env.txt): the entry path cannot fire there, and the defect is not driveable by any userland program (absent the card). Line-level proof of the unwind gap is in VERDICT.md. Family precedent: DF-5115 (ips, same shape) was filed untested for the same reason; DF-5141 (mlx) was reproduced only because its primitive was a genuine double-kfree driveable by a KLD harness β€” this one has no such primitive to harness.

Fix

See fix.diff: full reverse-order teardown on the bus_setup_intr failure path (free paths β†’ deregister buses β†’ free sims β†’ deregister the shutdown handler (tag now stored in the softc) β†’ dpt_free β†’ destroy parent_dmat), plus the correct io rid in bad: (that hunk overlaps DF-2151's fix and is included only because the unwind is meaningless while the release is a no-op).

VERDICT.md
↓ download raw

DF-5153 VERDICT β€” dpt_pci.c setup_intr-failure incomplete unwind

Status: untested (hardware-gated). Impact: none demonstrable on the audit guest (no DPT controller present β€” verified, env.txt). Confidence: certain that the defect exists as described (line-proven). Fix: authored, not kernel-validated (fix.diff; baseline behavior cannot be reproduced without the HBA, so fix validation is not_testable).

1. Root cause (line-certain)

  • sys/dev/raid/dpt/dpt_pci.c:151 β€” dpt_init(dpt) succeeds (DMA tags at dpt_scsi.c:1235/1356/1370, CCB array at :1385, sg maps at :1254).
  • sys/dev/raid/dpt/dpt_pci.c:159 β€” dpt_attach(dpt) registers up to 3 SIMs + wildcard paths with XPT and the shutdown_post_sync handler (dpt_scsi.c:1466-1488); return value (channel count) is ignored by the glue β€” harmless here since >0 channels means live registrations either way.
  • sys/dev/raid/dpt/dpt_pci.c:163-169 β€” bus_setup_intr failure jumps to bad: having undone nothing of the above.
  • sys/dev/raid/dpt/dpt_pci.c:173-179 β€” bad: releases only io/irq. The io release passes rid 0 for a resource allocated at rid PCIR_MAPS(=0x10)/PCIR_MAPS+4 β†’ pci_release_resource' resource_list_delete(rl, type, rid) lookup misses β†’ EINVAL no-op β†’ the io leaks (this half is DF-2151, known). The irq release at :176-177 uses rid 0, which matches the allocation rid at :103-105 β†’ genuinely released while the device keeps its registers programmed (busmaster was forced on at :113-114).
  • sys/kern/subr_bus.c:3328-3333 β€” bus_release_resource is a pure rid-keyed dispatch, confirming the asymmetry.

2. What stays alive (the defect)

Object Created at Released on failure?
XPT buses/sims dpt_scsi.c:1466-1473 never (phantom SIM)
CAM wildcard paths dpt_scsi.c:1475-1482 never
shutdown_post_sync handler (tag discarded) dpt_scsi.c:1487-1488 never
softc on global dpt_softcs dpt_scsi.c:1165 never
CCB DMA memory + 3 DMA tags dpt_init never (dpt_free not called)
io resource dpt_pci.c:88/94 no-op leak (DF-2151)
irq resource dpt_pci.c:104 released (correct rid) β€” while the card can still assert it

3. Reachability / threat model

Entry requires dpt_pci_probe to match PCI 0x1044:0xa400 (dpt_pci.c:61-66) β€” real DPT SmartRAID hardware β€” and bus_setup_intr to fail (interrupt vector exhaustion, MSI/MSI-X setup failure on the platform). Both are boot/kldload-time conditions. No unprivileged local user trigger exists; on hardware without the card the driver never attaches (verified on the guest: pciconf -l shows no 0x1044 device). uid=0 escalation is not applicable: no user-controlled grooming window, no kernel-heap primitive β€” the residual primitives are (a) a CAM-reachable bus_space handle whose rman record was released/re-allocatable, (b) an irq released under a busmaster-enabled device, (c) permanent leaks, (d) never-completing CAM commands (boot hang). Ceiling on real hardware: cross-device PIO/MMIO corruption and boot-time DoS.

4. Why untested (honest classification)

  • Guest has no DPT HBA (env.txt) β†’ dpt_pci_attach unreachable, and no userland program can reach any part of this path even with one present.
  • Unlike DF-5141 (mlx double-free β€” reproduced via KLD harness driving the real mlx_free() twice), the consequence here is logical (CAM/rman lifetime), not allocator-detectable: a harness that fabricates a softc and calls xpt_bus_register + bus_release_resource would only demonstrate that CAM keeps a sim whose resource was released β€” the code's own text β€” without adding evidentiary weight. Recorded as analysis-verified instead.

5. Fix

fix.diff (dpt_pci.c + dpt.h + dpt_scsi.c): reverse-order teardown on the failure branch β€” free paths, deregister buses, free sims, deregister the shutdown handler via a tag now stored in the softc, crit_enter; dpt_free(dpt); crit_exit(); bus_dma_tag_destroy(dpt->parent_dmat) (covers DF-5154 on this path); and the io rid correction in bad: (hunk shared with DF-2151's fix). Not kernel-build-validated (no target hardware; fix validation not_testable).

6. Kernel references

sys/dev/raid/dpt/dpt_pci.c:159, :163-169, :173-179, :88-94, :103-105, :113-114; sys/dev/raid/dpt/dpt_scsi.c:1165, :1235, :1356, :1370, :1385, :1466-1488, :1717-1733; sys/kern/subr_bus.c:3328-3333.

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 against the read-only sys/ tree; git apply --check clean (dpt.h + dpt_scsi.c tag plumbing + dpt_pci.c teardown). Baseline cannot be reproduced on the guest (no DPT HBA), so patched-kernel validation is not_testable.

findings/poc/DF-5153/fix.diff (3-file unified diff, apply-checked); VERDICT.md section 5.
↓ fix.diffper-fix-DF-5153

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md (bug + consequence table), VERDICT.md sections 1-2 with the stays-alive table, fix.diff (git-apply-clean reverse-order teardown), env.txt (guest has no DPT PCI device). Decisive lines: sys/dev/raid/dpt/dpt_pci.c:163-169 vs :159 and bad: at :173-179; dpt_scsi.c:1466-1488.

PoC changes

no seed PoC existed; none authored - path is unreachable without DPT hardware (KLD harness would only restate the code text; no allocator-detectable primitive unlike DF-5141)

Verified recommended fix

On bus_setup_intr failure, tear down in reverse order before bad: xpt_free_path each paths[ch], xpt_bus_deregister+cam_sim_free each sims[ch], EVENTHANDLER_DEREGISTER(shutdown_post_sync, tag stored in softc), crit_enter/dpt_free/crit_exit, bus_dma_tag_destroy(parent_dmat); release io with the rid it was allocated at (io_rid).

Verdict

Line-proven incomplete error-path unwind in dpt_pci_attach(): on bus_setup_intr failure (dpt_pci.c:163-169) the code jumps to bad: releasing only io/irq, while dpt_attach() (called at :159) has already XPT-registered up to 3 SIMs + wildcard CAM paths (dpt_scsi.c:1466-1482), registered the shutdown_post_sync handler with a discarded tag (dpt_scsi.c:1487-1488), and dpt_alloc() TAILQ-linked the softc on the global dpt_softcs (dpt_scsi.c:1165). None of that is undone: the phantom SIM stays resolvable by CAM and pokes bus_space through a handle whose rman record was released (irq, correct rid 0) or leaked (io, the DF-2151 wrong-rid no-op), dptshutdown keeps firing at every reboot against the released window, DMA CCB memory/sg maps/3 child tags leak permanently, and no interrupt handler exists so every command routed to the phantom bus hangs forever. Untested because the entry path is hardware-gated: guest pciconf shows no 0x1044:0xa400 DPT device (env.txt) and no unprivileged trigger exists; ceiling on real hardware is cross-device PIO/MMIO corruption at boot plus boot-time DoS. No kernel-heap primitive, so no uid=0 route - the class does not permit one.