# VERDICT — DF-3819

**Status: not_reproduced** (hardware-gated; source-certain defect)

## Root cause

`sys/dev/raid/aac/aac.c:aac_ioctl_send_raw_srb()`:

```c
3076:	cm = NULL;
...
3082:	if (aac_alloc_command(sc, &cm)) {
3083:		event = kmalloc(sizeof(struct aac_event), M_AACBUF, M_NOWAIT|M_ZERO);
...
3090:		event->ev_type = AAC_EVENT_CMFREE;
3091:		event->ev_callback = aac_ioctl_event;
3092:		event->ev_arg = &cm;                    /* waker will wakeup(&cm) */
3093:		aac_add_event(sc, event);
3094:		lksleep(cm, &sc->aac_io_lock, 0, "aacraw", 0);   /* BUG: cm == NULL */
```

The completion callback `aac_ioctl_event()` (aac.c:2959-2971) runs from
`aac_release_command()` (aac.c:1417-1420) when a FIB is freed, stores the
new command through `arg` and calls `wakeup(arg)` where
`arg == &cm` — the *address of the kernel stack slot*.  The sleeper,
however, sleeps on the channel whose *value* is `cm`, i.e. `NULL` at that
point (it is only assigned by the callback after the wakeup that never
reaches it).

The sibling function `aac_ioctl_sendfib()` gets it right:

```c
3003:		lksleep(&cm, &sc->aac_io_lock, 0, "sendfib", 0);
```

`lksleep()` without `PCATCH` is uninterruptible, and no other code path in
the driver does `wakeup(NULL)`, so the mismatched sleeper never wakes:
the calling thread hangs permanently in state `D` and cannot be killed.

## Trigger / reachability

- Device node `/dev/aacd0` (aliases `afa%d`, `hpn%d`) is created `0640
  root:operator` at attach (aac.c:317-320) — **no privilege check exists in
  `aac_open()`** (aac.c:2780-2791), so any member of group `operator` can
  reach the ioctl.
- The hang requires exactly one precondition: `aac_alloc_command()` returns
  `EBUSY` once (aac.c:1370-1377) — i.e. all FIBs outstanding, trivially
  arranged on real hardware by parallel management traffic.

## Why not reproduced on the guest

The QEMU audit guest has no Adaptec AAC PCI HBA:

- `ls /dev/aac* /dev/asr* /dev/afa* /dev/hpn*` → `No such file or directory`
  (env.txt, run.log)
- `pciconf -l` shows zero devices with aac vendor IDs
  (0x9005/0x9004 Adaptec, 0x1028 Dell PERC, 0x1044 DPT) — env.txt
- the aac driver is compiled into `X86_64_GENERIC`
  (`kldload aac` → "module aac/aacd already exists"), but attach never runs
  without hardware, so the control device never exists and the ioctl path
  cannot be entered at all.

The defect itself is deterministic and visible in the source: a one-token
typo (`lksleep(cm` vs `lksleep(&cm`), proven by direct comparison with the
correct sibling two hundred lines above.

## Fix

`fix.diff`: sleep on `&cm`. (Upstream FreeBSD's ancestor of this code uses
`tsleep(&cm, ...)` — the DragonFly port mangled the `&`.)

## Fix validation

Not possible on this guest (no hardware to attach the driver to);
`fix_status: not_testable`.

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