# DF-5046 — idad_detach frees the idad softc under open descriptors and in-flight bios (LD-family detach UAF)

File: `sys/dev/raid/ida/ida_disk.c` (pass 2, GLM 5.3)
Bucket: memcorrupt | Severity: Low | Pattern: DF-5013 (mfi_disk) / DF-5025 (ips_disk)

## What the bug is

`idad_detach()` (sys/dev/raid/ida/ida_disk.c:302-311) releases the softc's
registrations with no gate on open file descriptors and no drain of in-flight
I/O:

```c
static int
idad_detach(device_t dev)
{
	struct idad_softc *drv;

	drv = (struct idad_softc *)device_get_softc(dev);
	devstat_remove_entry(&drv->stats);
	disk_destroy(&drv->disk);
	return (0);
}
```

After it returns, the bus/devclass layer frees the `idad_softc`. Nothing in
the teardown path protects the two consumers of `dsk->si_drv1`:

1. **Open fds (DF-5025 leg).** `disk_destroy()` sends a synchronous
   DISK_DISK_DESTROY (sys/kern/subr_disk.c:876-880); the handler waits only
   for disk-enumeration refs `dp->d_refs` (subr_disk.c:540-542) and then
   destroys the cdevs (subr_disk.c:546-548). `destroy_dev()` ->
   `devfs_destroy_dev()` is *asynchronous* (sys/vfs/devfs/devfs_core.c:759-763)
   and only drops sysrefs; the cdev object survives while any open fd holds a
   reference, and neither that path nor `idad_detach` ever clears
   `si_drv1` or revectors it away from `id_ops`. A read/write on the still-open
   fd then runs `idad_strategy()` -> `idad_getsoftc()` (ida_disk.c:94-99)
   which returns the dangling softc pointer; the very next statements write
   through it (`devstat_start_transaction(&drv->stats)` ida_disk.c:174,
   `ida_submit_buf(drv->controller, bio)` ida_disk.c:175 — queue-head writes
   into freed memory).

2. **In-flight bios (DF-5013 leg).** `dev_dstrategy()` takes a
   `bio_track_ref()` on `dev->si_track_*` (sys/kern/kern_device.c:375-380) —
   this keeps the *cdev* alive, not the softc. `bio_track_wait()` exists
   (sys/kern/vfs_bio.c:565) but is called only from vnode-drain paths
   (sys/kern/vfs_subr.c:326,377,526,799); no cdev-destroy path drains it. A
   bio completing after detach runs `idad_intr()` (ida_disk.c:213-226) ->
   `devstat_end_transaction_buf(&drv->stats, bp)` on the freed softc.

## Threat model / trigger

`idad_detach` is reachable only via the device detach path (module unload of
the in-kernel `ida`/`idad` drivers — `kldstat -v` on the standard X86_64_GENERIC
guest shows `ida/idad`, `pci/ida` compiled in). Requires root (kldunload) —
hence Low, same rating rationale as DF-5025. (DF-5013 was High only because
mfi had an *unprivileged-time* AEN offline path that deleted disks at runtime;
ida has no runtime delete path and no ioctls at all.)

## PoC / verification status

Hardware-gated: the ida driver attaches only to Compaq SMART/IDA PCI boards
(ida_pci.c:146-170 board_id[] = subdevice ids 0x40300E11..0x40580E11,
vendor 0x0E11, matched at ida_pci.c:199-210). The audit QEMU guest has no
Compaq PCI device (`pciconf -l | grep -i 0e11` empty — see env.txt), so no
`/dev/idad*` node exists and the trigger cannot be exercised. The code trace
above is line-accurate and reproduces the exact DF-5025 family defect.

## Fix

Gate detach on an open count (and let the controller side quiesce per
DF-1915's fix). See fix.diff.

## Reproduce (on hardware)

```
# on a machine with a Compaq SMART/IDA controller:
dd if=/dev/idad0 of=/dev/null bs=64k count=100 &   # keep fd open + I/O in flight
kldunload ida_pci (or detach path via hw.pci....)  # as root, while the above runs
# observe: idad_strategy / idad_intr touch freed softc (KASAN/MEMGUARD or panic)
```
