# VERDICT — DF-3822

**Status: inconclusive** (race requires an attached AAC controller;
none on the audit guest).  **Source-level confidence: certain** for the
locking asymmetry; **likely** for exploitability end-to-end (window is
narrow but device-driven events are repeatable).

## Lock-state matrix for `sc->aac_container_tqh`

| actor | trigger | lock held while walking | lock held while removing |
|-------|---------|-------------------------|--------------------------|
| `aac_handle_aif` AIF re-enum (aac.c:3269-3326) | adapter interrupt (aac.c:842) or aif thread (aac.c:1054) | **none** | `aac_container_lock` (aac.c:3316) |
| `aac_query_disk` (aac.c:3665) | operator ioctl | `aac_container_lock` | n/a (walk only) |
| `aac_detach` (aac.c:679-685) | root kldunload/detach | **none** | **none** |

The interrupt handler is still registered while detach runs the container
loop: `bus_teardown_intr` happens inside `aac_free()` (aac.c:625-626),
*after* the loop; interrupt masking happens in `aac_shutdown()` (:767)
which detach calls at :696 — also after the container loop.  So an AIF
landing during the loop runs `aac_handle_aif()` concurrently with the
unlocked `TAILQ_REMOVE`/`kfree(co)` at :683-684:

- `TAILQ_FOREACH(co, &sc->aac_container_tqh, co_link)` at :3269 reads
  `co_link` of an entry freed at :684 → UAF read → bogus pointer →
  `co->co_mntobj.ObjectId` deref at :3272 → arbitrary read/panic.
- `aac_query_disk` under the lock can equally deref memory freed by the
  unlocked detach loop.

Additionally the *walk* in `aac_handle_aif` (:3269-3278, :3306-3326)
itself runs without the container lock even against its own removal
phase at :3316 — the code drops/retakes `aac_io_lock` and mplock around
`device_delete_child` (:3309-3314) but the `TAILQ_NEXT(co, co_link)` at
:3315 can already point at an entry another CPU removed.

## Why not reproduced

Race needs: (1) AAC controller attached — impossible on the guest
(zero matching PCI devices, no `/dev/aacd0`); (2) root detach concurrent
with a device AIF or operator ioctl.  Nothing to stage on the audit
guest beyond proving absence (env.txt).

## Fix

`fix.diff` wraps detach's removal/free in `aac_container_lock`, matching
the discipline `aac_handle_aif` already uses for its removals.  (A full
fix would also lock the `aac_handle_aif` walk itself; the diff keeps the
minimal, obviously-correct delta and notes this.)

## Fix validation

`not_testable` — same hardware precondition.

## 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.
