# DF-2420 VERDICT — unchecked ahci_ata_get_xfer() NULL in both IO dispatchers

## Bottom line
**REPRODUCED on live AHCI hardware via the unmodified production path**: with
the per-port ccb free list empty, a single ordinary pass(4) XPT_SCSI_IO CCB
panics the kernel inside the production dispatchers — the exact NULL
derefs the finding describes. The free-list exhaustion that occurs
naturally when a device's ncqdepth < sc_ncmds (probe prune loop) cannot be
produced by QEMU's emulated disk (it advertises ncqdepth 32 == sc_ncmds),
so the exhaustion state was produced on the REAL port by holding every
free ata_xfer through the driver's own exported symbols.

## Live evidence (run.log, panic-disk.txt, panic-atapi.txt)
* Port 0 (DISK da0/pass0) drained to `ap_ccb_free EMPTY` → one READ_10
  pass CCB →
  `Stopped at ahci_xpt_scsi_disk_io+0x8f9: movl $0x1,0x60(%r12)` —
  fault VA **0x60**, supervisor **WRITE** = `xa->flags = ATA_F_READ`
  (ahci_cam.c:1334) with xa == NULL (r12=0), i.e. the very first
  post-switch deref in the READ_10 path.
* Port 1 (ATAPI cd0/pass1) drained → one 6-byte ATAPI CCB →
  `Stopped at ahci_xpt_action+0x2d6: movq (%rax),%rax` — fault VA **0x0**,
  supervisor READ = `fis = xa->fis` (ahci_cam.c:1525, inlined) with
  xa == NULL.
* Both panics are in production code reached through the ordinary CAM
  pipeline (user ioctl → passsendccb → xpt_action → ahci_xpt_action →
  dispatcher); the rig (df2420_rig.c) only manipulated free-list state
  beforehand, using the exported get/put symbols.

## Negative control (live churn, flood2.log)
48 concurrent full-disk reads (48 x 512MB) + 19,639 pass READ_10 CCBs: no
crash, all clean. On this hardware `tags=32/32` — the prune loop
(ahci_cam.c:386-392) keeps all 32 slots free-listed and CAM's devq
(cam_simq_alloc(sc_ncmds-1), ahci_cam.c:112-115) never exhausts them.
This pins the natural precondition to real disks reporting ncqdepth <
ncmds (e.g. queue depth 8/16 SATA drives, exactly as the finding states),
or >ncmds-1 simultaneous in-flight commands on such pruned ports.

## Impact
Immediate kernel panic (local DoS) on AHCI-attached storage under slot
exhaustion; any user issuing disk I/O when the port is in that state hits
it (the CCB source — filesystem, raw device, pass — is irrelevant).

## Fix validation (fix.diff: NULL checks + CAM_RESRC_UNAVAIL in both
dispatchers)
Patched kernel re-built and booted; identical drain + pass CCB trigger:
no panic, CCB returns cleanly (CAM_RESRC_UNAVAIL), subsequent normal I/O
on the port works after `sysctl dev.df2420rig=release`.

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