# DF-5377 VERDICT

## Status: REPRODUCED (permanent pbuf leak → system-wide raw-I/O DoS),
## fix validated (after two live-refuted intermediate fix designs)

## Root cause
`targsendccb()` maps the daemon's CTIO data buffer through
`cam_periph_mapmem()` (scsi_target.c:788; cam_periph.c:643-767), which
allocates a bounce pbuf with `getpbuf_mem()` and records it in
`descr->mapinfo`. The only release site is `targreturnccb()`
(scsi_target.c:973-974). Two teardown paths free CCBs *without*
releasing the mapping:

- `targdone()` not-enabled path (scsi_target.c:828-832):
  `targfreeccb(softc, done_ccb)` — drops `mapinfo->bp[0]`;
- `targdtor()` user_ccb_queue drain (scsi_target.c:541-544): same.

Each dropped pbuf is never returned to the pool
(`vm.pbuf_mem_count`, 394 on this guest); once the pool is empty every
`getpbuf_mem()` caller sleeps forever in `tsleep(..., "wswbuf5")`
(vm/vm_pager.c:505-528) — raw physio I/O (dd/fdisk/label ops on any
disk) wedges system-wide until reboot.

## Guest evidence (baseline kernel #0, targsim rig)
Choreography per cycle on one fd (no race, no close → DF-1045 not
involved): TARGIOCENABLE → write() CTIO (CAM_DIR_IN, dxfer 512) →
targsim holds it → TARGIOCDISABLE (rig fails the XPT_ABORT with
CAM_UA_ABORT so the CTIO survives; EN_LUN disable clears
LUN_ENABLED) → sysctl hw.targsim.complete=1 → rig xpt_done()s the
held CTIO → targdone sees not-enabled → targfreeccb without unmap.

- run.log: `vm.pbuf_mem_count: 394` → 386 after exactly 8 cycles,
  never recovers; `hw.targsim.held_total: 8`.
- run.drain.log: 386 further cycles → `pbuf_mem_count=0`;
  `dd if=/dev/vbd0 of=/dev/null bs=64k count=1` then sits **unkillable
  in D0 on wchan `wswbuf5`** — permanent system-wide raw-I/O wedge.

## Fix engineering (documented honestly)
- v1 (cam_periph_unmapmem() inside targdone): REFUTED live — targdone
  runs in the cambio SWI where `copyout()` is illegal: `Fatal trap 12
  at copyout+0x14` (current process Idle).
- v2 (relpbuf-only inside targdone): REFUTED live — the pbuf is
  `BUF_LOCK()`ed by the mapping thread: `panic: lockmgr: pid -1, not
  exclusive lock holder` at `relpbuf ← targdone ← camisr_runqueue`.
- v3 (validated): route not-enabled completions to `user_ccb_queue`
  (per the documented API contract in scsi_targetio.h) and
  `cam_periph_unmapmem()` in `targdtor()` (daemon thread context,
  where copyout and the pbuf unlock are legal).

## Fix validation (kernel #3, Sat Oct 3 18:32:37 2026)
`./poc5377 8 x readback` on the patched kernel: at most one pbuf
outstanding during the run (393 at the measurement point), pool back
to **394 baseline** with zero permanent loss; guest healthy.
`[+] FIXED: pool fully restored after daemon read the completions`.
versus baseline −1-per-cycle-forever and the 394→0 drain wedge.

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