# DF-3771 VERDICT — not_reproduced (guest lacks AHCI hardware); defect certain by source analysis

## Bottom line

`ahci_port_free()` (sys/dev/disk/ahci/ahci.c:979-1046) frees the per-port CCB
array, DMA maps, `ap_ata[]` and the `ahci_port` itself without (a) cancelling
any armed `ccb->ccb_timeout` callouts and without (b) failing/completing CCBs
still queued in `ap_ccb_pending` or active in `ap_active`/`ap_sactive`.
Any CCB that was on-chip or pending at detach leaves a live callout pointing
into freed memory. The callout body,
`ahci_ata_cmd_timeout_unserialized()` (ahci.c:3789-3799), writes
`ccb->ccb_xa.flags` (offset into the freed `ap_ccbs` block) and then calls
`ahci_os_signal_port_thread(ap, ...)` (ahci.c:3798) which executes
`lockmgr(&ap->ap_sig_lock, LK_EXCLUSIVE)` + `wakeup(&ap->ap_thread)`
(ahci_dragonfly.c:306-312) on the freed `ahci_port` — use-after-free write
plus a lock operation on reallocated kernel heap.

This is exactly the teardown discipline `ahci_port_hardstop()` implements for
the hot-unplug path (ahci.c:1884-1936: walk `ap_active`/`ap_sactive`/
`ap_ccb_pending`, `callout_cancel` each, complete each CCB) — the detach path
simply never performs the equivalent cleanup.

## Why it was not reproduced (environment, not doubt)

- Guest `uname -a`: DragonFly dfbsd 6.5-DEVELOPMENT #0 X86_64_GENERIC (see env.txt)
- `pciconf -l`: **no AHCI/SATA controller present**; root disk is virtio-blk
  (`vbd0s1d`, hammer2).
- ahci is compiled into X86_64_GENERIC (`kldstat -v` shows `27 pci/ahci`
  registered from the static kernel) — there is no ahci.ko to kldunload, and
  DragonFly has no runtime PCI driver unbind, so the detach path cannot be
  entered on this guest at all.
- No QEMU knob in `dfbsd-qemu/vm.sh` to attach an ich9-ahci device, and DFly
  does not rescan PCI at runtime, so hot-adding one via the monitor would not
  attach the driver either.

Classification: `not_reproduced`, `reproduced=0`, `impact=none` **on this
guest**. The defect itself is certain by inspection (no cancellation code
exists anywhere on the free path — verified by
`rg 'callout_' sys/dev/disk/ahci/ahci.c`: cancel sites are 1892/1913/3764 only,
all outside `ahci_port_free`).

## Exploit chain (theoretical)

Root-only trigger (module unload), so privesc is moot. Primitive: UAF write of
flag bits + lockmgr on freed-but-reallocatable `ahci_port` (~2.4 KB kmalloc,
M_DEVBUF zone shared with many drivers). Realistic outcome: panic or silent
corruption; a determined attacker with root already has simpler paths.
Severity therefore Low.

## Recommended fix

In `ahci_port_free()`, before freeing anything: walk `ap_active` and
`ap_sactive`; for each slot cancel the callout (`callout_cancel_sync`) and
complete the CCB with `ATA_S_TIMEOUT` via a small helper
(`ahci_port_free_fail_ccb()`), mirroring `ahci_port_hardstop()`. Pending-queue
CCBs need no callout handling (their callouts are only armed at the ONCHIP
transition — ahci.c:2378-2379/2444-2445 — and their CAM ccb leak is bounded by
the pool size).

fix.diff included (generated from a scratch git worktree; verified to apply
cleanly against the pristine tree with `git apply --check`; never applied to
sys/).

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