# DF-5375 VERDICT

## Status: REPRODUCED (panic via injected queued XPT opcode), fix validated

## Root cause
`targwrite()` (scsi_target.c:584-632) accepts ANY 32-bit `func_code`
fetched with `fuword32()` from the daemon's CCB. Non-ATIO/INOT codes
take the default branch where only `XPT_FC_QUEUED` is checked; queued
codes are forwarded through targstart → targusermerge (whole-CCB
copyin from user memory) → targsendccb → `xpt_action()`. `targdone()`
(scsi_target.c:837-850) — the completion callback stamped on every
forwarded CCB — only expects INOT/ATIO/CTIO and
`panic("targdone: impossible xpt opcode %#x")` otherwise. FreeBSD's
rewritten scsi_target.c restricts the injectable opcodes for exactly
this reason; DragonFly's 2003-vintage driver never got that filter.

Secondary consequence of the same missing whitelist (not separately
PoC'd): opcodes whose payloads contain kernel pointers — XPT_SCSI_IO
with CAM_CDB_POINTER (`cdb_io.cdb_ptr`), `data_ptr`, XPT_DEV_MATCH
cookies — are forwarded raw because `targsendccb()` maps only
CTIO/DEV_MATCH (scsi_target.c:783-799); SIMs then dereference
daemon-supplied kernel addresses (CWE-822).

## Guest evidence (baseline, targsim rig, bus 2)
`./poc5375` — TARGIOCENABLE(targsim 0:0:0) then write() of a CCB with
`func_code = XPT_SCSI_IO (0x901)`:
- `[TARGSIM] EN_LUN enable=1 grp6=0 grp7=0 -> OK`
- `[TARGSIM] SCSI_IO via targ (descr=0xfffff801167edd40): completing`
- `panic: targdone: impossible xpt opcode 0x901`
- backtrace: `targdone() <- camisr_runqueue() <- swi_cambio()`
  (panic.txt, run.log). Deterministic; the SWI fires within
  milliseconds of the write(2).

## Impact
Reliable kernel panic (local DoS) from the target daemon with a single
write(2); plus a pointer-trust path turning daemon CCB contents into
raw kernel-address dereferences by the transport. The daemon normally
runs as root; the panic is therefore a robustness/availability break of
the target subsystem (and full host DoS), not an unpriv escalation on
the stock guest (device 0600 root:wheel, verified).

## Fix validation
fix.diff whitelists func codes at the top of the write loop
(ATIO/INOT/CTIO/ABORT allowed; anything else → EINVAL, write(2)
returns the error).
Patched kernel (#2): `write returned -1 (errno=22 EINVAL) - no panic`,
guest alive, `[-] still alive - NOT reproduced`. FIXED.

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