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)
PoC verification
Evidence pack
findings/poc/DF-5115 Β· 7 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 analysistrigger.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 resultfix.diffβ minimal fix: movesc->configured = 0after 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.
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
sys/dev/raid/ips/ips_pci.c:195-210(ips_pci_detach)sys/dev/raid/ips/ips_pci.c:213-221(ips_pci_shutdowngated onsc->configured)- EBUSY sources:
sys/dev/raid/ips/ips.c:525-537(ips_adapter_free:IPS_DEV_OPEN,ips_diskdev_free()failure,ips_cmdqueue_free()failure whenused_commands != 0)
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)
-
Shutdown data loss: after one failed detach,
ips_pci_shutdown(ips_pci.c:218-219) skipsips_flush_cache()forever β the adapter's volatile write cache is never flushed on reboot/halt. -
IRQ-handler use-after-free: every subsequent detach attempt takes the
configured == 0path and returns 0 without runningips_adapter_freeorips_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 holdsscas its argument. The next adapter interrupt callsips_morpheus_intr/ips_copperhead_intron 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_testablefix.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
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.
No comments yet.