# DF-3771 — ahci_port_free(): armed CCB timeout callouts + in-flight CCBs not cancelled/drained on detach → use-after-free

File: `sys/dev/disk/ahci/ahci.c` (pass 2, GLM 5.3)

## What the bug is

`ahci_port_free()` (ahci.c:979-1046) tears a port down on driver detach but:

1. **Never cancels the per-CCB timeout callouts.** Every non-polled command arms
   `ccb->ccb_timeout` via `ahci_start_timeout()` (ahci.c:2212-2220, timeouts are
   CAM defaults, typically 30-60 s). The callout is only cancelled on the
   completion/error paths (`ahci_ata_cmd_done` ahci.c:3762-3771,
   `ahci_port_hardstop` ahci.c:1884-1927). `ahci_port_free` has **no**
   `callout_cancel()` at all before `kfree(ap->ap_ccbs)` (ahci.c:1013) and
   `kfree(ap)` (ahci.c:1044).

2. **Never fails/drains CCBs still in `ap_ccb_pending` / `ap_active` /
   `ap_sactive`.** Contrast `ahci_port_hardstop()` (ahci.c:1884-1936) which
   walks all three masks, cancels callouts and completes each CCB — nothing
   equivalent runs before the memory is freed. Pending CCBs leak (their CAM
   ccb is never `xpt_done`'d), and active CCBs keep armed callouts.

After the frees, a still-armed callout fires `ahci_ata_cmd_timeout_unserialized()`
(ahci.c:3789-3799) which **writes** `ccb->ccb_xa.flags` in freed heap and calls
`ahci_os_signal_port_thread(ap)` → `lockmgr(&ap->ap_sig_lock, ...)` +
`wakeup(&ap->ap_thread)` on the freed `ahci_port` — a kernel use-after-free
write + lock operation on reallocated memory.

## Trigger / preconditions

- ahci built as a loadable module (`sys/dev/disk/ahci/Makefile` → ahci.ko) and
  `kldunload ahci` while a command is in flight (slow/hung device, or mid-I/O
  unload). Requires root.
- Note: the stock X86_64_GENERIC kernel compiles ahci in, so the detach path is
  reachable only on module builds (or a custom kernel with ahci omitted, then
  kldload/kldunload). This narrows practical reachability — rated Low.

## Why not reproduced on the guest (honest status)

The audit guest has **no AHCI controller at all** — it boots from virtio-blk
(`vbd0s1d` on hammer2), `pciconf -l` shows no SATA/AHCI class device, and ahci
is compiled into the kernel (not unloadable) — see `env.txt`. The trigger
requires in-flight ahci I/O + runtime driver detach, neither of which exists on
this guest. Source-level certainty only; verdict `not_reproduced` (device
absent).

## Reproduce on suitable hardware (for upstream)

1. Build kernel without ahci; build ahci.ko.
2. Attach a SATA disk (ideally one that can be made to hang mid-command, e.g.
   `sg`-style stall or a delay-injecting bridge).
3. Saturate the disk with I/O; `kldunload ahci` while commands are on-chip. (e.g.
   a delay-injecting bridge, or simply unplug the disk mid-I/O then unload)
4. Within `ccb->ccb_xa.timeout` seconds the freed-CCB callout fires → UAF
   (panic with debug.mpsafe; silent corruption otherwise).
