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

ips_pci_detach clears sc->configured before ips_adapter_free can return EBUSY: one failed detach permanently corrupts the state machine β€” retried detaches silently succeed without ips_pci_free, leaving the adapter IRQ handler registered against softc freed at module-unload unit destruction (interrupt-context UAF read/write), and ips_pci_shutdown permanently skips the cache flush

Summary

configured is the only detach/shutdown gate yet is cleared before the cleanup that can fail EBUSY (open ipsd / used_commands) - after one EBUSY every retry returns 0 without ips_pci_free, the IRQ keeps sc as handler arg, and unit destruction frees the softc under the live handler; shutdown also never flushes the adapter cache again. Root+hardware-gated (no ServeRAID on guest - proven). Keep-configured-until-success fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5115 Β· 7 files
FileTypeDescriptionSize
README.md β€” 1.3 KB ↓ raw
VERDICT.md β€” 3.8 KB ↓ raw
trigger.sh β€” 1.5 KB view raw
env.txt β€” 1.2 KB view raw
fix.diff β€” 645 B view raw
verdict.json β€” 3.5 KB view raw
SHA256SUMS.txt β€” 152 B view raw

DF-5115 evidence pack

Finding: ips_pci_detach() clears sc->configured before the cleanup that can still fail with EBUSY β€” after one failed detach the driver's state machine is permanently corrupted: shutdown stops flushing the adapter cache, and any retried detach silently succeeds without tearing down the interrupt, so the softc is later freed with sc->ips_adapter_intr(sc) still registered (interrupt-context UAF).

Status: untested β€” reproduction requires a physical IBM ServeRAID controller; the audit QEMU guest provably has none (see env.txt, trigger.sh exits 42 there).

Contents

  • VERDICT.md β€” full code trace and consequence analysis
  • trigger.sh β€” hardware-gated trigger script (exit 42 without ServeRAID)
  • env.txt β€” live guest evidence: uname, pciconf (no 1014:01bd/002e, 9005:0250), kernel config, kldload probe result
  • fix.diff β€” minimal fix: move sc->configured = 0 after the EBUSY check

Reproduce (on real hardware only)

sh trigger.sh          # expects: 1st kldunload EBUSY, 2nd returns 0 (bug)

Success criterion: second kldunload ips returns 0 while the adapter IRQ is still installed; the next interrupt (or unit destruction + interrupt) hits ips_morpheus_intr/ips_copperhead_intr on freed memory β†’ panic with ips backtrace.

VERDICT.md
↓ download raw

DF-5115 β€” ips_pci.c detach clears configured before EBUSY can abort: state permanently corrupted β†’ live IRQ handler UAF after a retried detach

Verdict: UNTESTED β€” trigger requires physical IBM ServeRAID hardware; the single audit guest has none (proved below). Static logic is certain.

Where

Root cause

    if (sc->configured) {
        sc->configured = 0;              /* <-- cleared BEFORE cleanup    */
        ips_flush_cache(sc);
        if (ips_adapter_free(sc))
            return EBUSY;            /* <-- detach fails, state stays 0 */
        ips_pci_free(sc);
    }
    return 0;

sc->configured is the only state gate for detach/shutdown (it is referenced nowhere else in the ips family β€” verified with rg over sys/dev/raid/ips/*.c). Clearing it before knowing cleanup succeeded corrupts the driver state machine on every failed detach.

Consequences (two)

  1. Shutdown data loss: after one failed detach, ips_pci_shutdown (ips_pci.c:218-219) skips ips_flush_cache() forever β€” the adapter's volatile write cache is never flushed on reboot/halt.

  2. IRQ-handler use-after-free: every subsequent detach attempt takes the configured == 0 path and returns 0 without running ips_adapter_free or ips_pci_free. The interrupt installed at attach (ips_pci.c:131-133, bus_setup_intr(..., sc->ips_adapter_intr, sc, ...)) is never torn down. newbus completes the detach; on module unload the devclass unit is destroyed and the softc is freed, while the registered handler still holds sc as its argument. The next adapter interrupt calls ips_morpheus_intr/ips_copperhead_intr on freed memory β€” interrupt- context UAF read/write. After the module text itself is unmapped, the handler vector is dangling and any interrupt faults.

The ipsd disk children are also orphaned (their device_delete_child in ips_diskdev_free is never re-attempted) and are force-deleted during unit destruction while possibly open/mounted.

Trigger (needs hardware)

On a machine with a real ServeRAID adapter, as root:

# keep a disk busy so the FIRST detach fails EBUSY
dd if=/dev/zero of=/dev/serno/ipsd0s0 bs=64k count=100000 &
kldunload ips        # -> EBUSY, but sc->configured is now 0
kldunload ips        # -> "succeeds"; IRQ still live, softc freed at unit delete
# wait for adapter IRQ -> ips_*_intr() on freed softc

Why untested on this audit's guest

pciconf -l shows no IBM (0x1014:0x01bd / 0x1014:0x002e) or Adaptec (0x9005:0x0250) ServeRAID device (see hwabsence.log). The ips driver is compiled into X86_64_GENERIC (config line 133) and ips.ko loads, but probe never matches, so no softc, no attach, no detach path exists. This finding is therefore filed from static analysis with status=untested.

Not a duplicate

  • DF-5025 (ipsd open/detach TOCTOU, ips_disk.c): userland racing a successful detach β€” different defect, different file.
  • DF-4613/DF-4614/DF-4640: ips.c/ips_commands.c lifecycle (deadlock, cmdqueue double-free, attach-timeout UAF) β€” this finding is purely the PCI-glue state machine that turns a failed detach into a silent success on retry.

Fix

Minimal: keep configured set until cleanup fully succeeds (fix.diff).

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 is a 2-line move authored against the read-only sys/ tree after static verification; it cannot be build-validated on this guest because the buggy path needs ServeRAID hardware to even instantiate the driver. The patch keeps configured=1 on EBUSY, restoring correct shutdown flush and full unwind on retried detach.

fix.diff; ips_pci.c:202-208 before/after shown in VERDICT.md
↓ fix.diffper-fix-DF-5115

Confirmed kernel references

Detail

Exploit chain

requires hardware: root holds ipsd busy -> kldunload ips (EBUSY, configured:=0) -> kldunload ips again (returns 0, IRQ live, softc freed at unit delete) -> next adapter interrupt -> ips_morpheus_intr/ips_copperhead_intr on freed softc -> interrupt-context UAF read/write (crash; heap grooming theoretical)

Evidence (decisive lines)

VERDICT.md (full trace); env.txt (guest pciconf shows no 1014:01bd/1014:002e/9005:0250 device; ips.ko loads but never probes); trigger.sh (hardware-gated trigger, exit 42 on this guest)

PoC changes

n/a - seed sketch could not run at all: the guest lacks ServeRAID hardware, so no ips device instance ever exists; trigger.sh encodes the hardware-gated sequence instead

Verified recommended fix

Move sc->configured = 0 to after the ips_adapter_free EBUSY check in ips_pci_detach so a failed detach leaves the state machine intact for shutdown flushing and a full unwind on retry.

Verdict

Static logic is certain and fully traced: ips_pci_detach (ips_pci.c:202-208) clears sc->configured BEFORE ips_adapter_free can return EBUSY, so after any failed detach (open ipsd disk / IPS_DEV_OPEN / nonempty command queue, ips.c:529-537) the driver is permanently wedged in the detached state: ips_pci_shutdown skips the cache flush (ips_pci.c:218) and every retried detach takes the configured==0 path returning success WITHOUT ips_adapter_free/ips_pci_free - the IRQ installed at attach (ips_pci.c:131-133) keeps sc as handler argument past softc destruction, giving an interrupt-context UAF read/write once the unit is deleted (kldunload retry). Not reproduced because the single audit guest has no IBM/Adaptec ServeRAID controller (pciconf evidence in env.txt); probe never matches so the code path has no instance to run on. Root-privileged trigger + hardware requirement cap severity at Low.