# DF-3822 — aac_detach() frees the container list unlocked while the interrupt path and user ioctls walk it (detach vs in-flight AIF → use-after-free)

- **File:** `sys/dev/raid/aac/aac.c`
- **Lines:** 678-685 (detach, unlocked removal/free), 3269-3326
  (`aac_handle_aif` list walks), 3306-3326 (its locked removals),
  3664-3687 (`aac_query_disk` locked walk), 625-626 (interrupts torn down
  only later, inside `aac_free`)
- **Class:** CWE-362/CWE-416 — missing-lock list teardown → UAF
- **Reach:** root-triggered `kldunload`/detach racing a device-initiated
  AIF re-enumeration (`AifEnAddContainer`/`AifEnDeleteContainer`) or a
  concurrent `FSACTL_QUERY_DISK` ioctl.

## Analysis

`aac_detach()` removes and frees every container **without**
`aac_container_lock`:

```c
679:	while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) {
680:		error = device_delete_child(dev, co->co_disk);
681:		if (error)
682:			return (error);
683:		TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link);
684:		kfree(co, M_AACBUF);
685:	}
```

Meanwhile:

- `aac_handle_aif()` — reachable from `aac_new_intr()` while interrupts
  are still live (they are masked only later, in `aac_shutdown()` at
  :696/767, and `bus_teardown_intr` happens even later inside
  `aac_free()` at :625) — walks `aac_container_tqh` with **no**
  `aac_container_lock` (aac.c:3269-3278) and takes it only around its own
  removals (aac.c:3316-3319).
- `aac_query_disk()` (operator-reachable ioctl) walks the same list under
  `aac_container_lock` (aac.c:3664-3667) — serialized against
  `aac_handle_aif`'s removals but **not** against detach's unlocked ones.

Interleaving detach's `TAILQ_REMOVE`/`kfree(co)` with either walk yields
a `TAILQ_FOREACH`/`TAILQ_NEXT` dereference of freed `co` (UAF read →
`co->co_mntobj` copy / `device_get_softc(co->co_disk)` on freed memory),
or list corruption when both sides remove concurrently.

## Preconditions (why Low)

detach requires root, and the racing AIF requires the controller to send
an add/delete-container event inside the window; the `query_disk` variant
needs an operator-group user holding the lock at the right moment.  The
code defect (no lock on a shared-list teardown) is, however,
unambiguous.

## Verdict

`inconclusive` — needs AAC hardware to attach (absent on the audit
guest, ../DF-3819/env.txt); the race cannot be staged without it.
Source-level: certain (locking asymmetry provable by inspection).
