# DF-5376 VERDICT

## Status: REPRODUCED (arbitrary-address kernel write; kernel-global
# queue corruption / RIP-control chain proven to the SWI boundary)

## Root cause
`targusermerge()` (scsi_target.c:720-745) translates the daemon's
userland XPT_ABORT `abort_ccb` pointer to the matching kernel CCB on
pending_ccb_queue; on no match it sets CAM_PATH_INVALID and returns
**ESRCH**. The inline send path tests only `!= EFAULT`
(scsi_target.c:626), so the ESRCH result is ignored and the CCB is sent
with the raw, untranslated user pointer. `xpt_action(XPT_ABORT)`
(cam_xpt.c:3075-3124) then dereferences that value as a kernel
`union ccb`:

  * branch guard: `XPT_FC_IS_DEV_QUEUED(abort_ccb)` reads
    attacker-controlled `func_code`; `pinfo.index >= 0` /
    `== CAM_UNQUEUED_INDEX` reads attacker-controlled index;
  * branch 2 (index == CAM_UNQUEUED_INDEX, status without
    CAM_SIM_QUEUED): writes
    `abort_ccb->ccb_h.status = CAM_REQ_ABORTED|CAM_DEV_QFRZN` at the
    attacker-chosen address, then `xpt_freeze_devq(abort_ccb->ccb_h.path, 1)`
    does `path->device->qfrozen_cnt += 1` — an arbitrary-address
    kernel increment through a fully attacker-wired fake
    path→device chain;
  * branch 1 (index >= 0): additionally
    `cam_ccbq_remove_ccb(&path->device->ccbq, ccb)` heap/TAILQ surgery,
    then `xpt_done(abort_ccb)` (cam_xpt.c:4891) reads
    `path->periph->type`, spin-locks the fake `sim->sim_spin`,
    TAILQ-inserts the fake CCB on the fake sim's doneq, and links the
    **fake struct cam_sim onto the kernel-global cam_simq**
    (`TAILQ_INSERT_TAIL(&cam_simq, sim, links)`), waking the cambio
    SWI. `camisr()` → `camisr_runqueue()` then drains the fake doneq
    and ends with the indirect call
    `(*ccb_h->cbfcnp)(ccb_h->path->periph, ccb_h)` — attacker-chosen
    function pointer with attacker-chosen first argument.

## Guest evidence
Mode A (clean, guest survives, run.log):
- fake CCB in an mmap'd page: func_code = XPT_SCSI_IO (0x901),
  pinfo.index = CAM_UNQUEUED_INDEX, fake path→bus→sim (sim->lock =
  the real targsim lock, held by targwrite, satisfying
  xpt_freeze_devq's `KKASSERT(lockowned())`), fake path→device with
  qfrozen_cnt = 0x11110000 and an empty active_ccbs list.
- after write(2): `fake ccb_h.status = 0x42
  (CAM_REQ_ABORTED|CAM_DEV_QFRZN)` and
  `fake dev->qfrozen_cnt = 0x11110001`; write() returned 8 and the
  daemon's CCB copyout showed CAM_REQ_CMP — the kernel executed the
  entire fake-CCB walk and both writes landed at user-chosen
  addresses.

Mode B (RIP-control chain, panic.b.txt / run.b.log):
- fake pinfo.index = 1 → branch 1: xpt_done(fake) links the fake sim
  onto kernel-global cam_simq during write() (process context — the
  user-page writes succeed), setsoftcambio() fires.
- The SWI wakes and starts walking the corrupted cam_simq:
  `Fatal trap 12 ... fault virtual address = 0x8004a1248
  (supervisor write)` — that address is **fake_sim + 0x48 inside the
  daemon's mmap page** — `Stopped at swi_cambio+0x59:
  movq %rdx,0x48(%rax)`. The final `cbfcnp` call did not execute only
  because DF kernel threads run without the user pmap mapped; the
  faulting access is the SWI operating on the attacker-supplied fake
  sim. Relocating the fake tree into kernel-addressable memory — e.g.
  inside one of the kernel CCBs whose payloads `targusermerge()`
  copyin()s verbatim from the daemon (heap-groomed; the DF-2286
  cbfcnp/path copyout class yields the needed kernel addresses, no
  KASLR on this guest) — removes that incidental barrier and turns the
  chain into a call of 0x414141414140 (kernel RIP control).

## Impact
The daemon (root on the stock guest) converts one write(2) into:
(1) a write/increment at an arbitrary kernel address (proven clean,
mode A), and (2) corruption of the kernel-global CAM completion queue
with fully attacker-controlled fake structures leading to an
attacker-chosen indirect call (proven to the SWI boundary, mode B).
This is kernel memory corruption (CWE-822/CWE-787-class), not merely a
panic. uid=0 from an *unprivileged* user is not reachable through this
device on the stock guest (0600 root:wheel + SYSCAP_RESTRICTEDROOT on
open, verified live); for the privileged daemon the primitive is
directly weaponizable.

## Fix validation
fix.diff changes the check to `== 0` so any targusermerge() failure
(copyin fault OR ESRCH translation failure) aborts the send.
Patched kernel (#2):
- mode a: `fake ccb_h.status = 0`, `qfrozen_cnt = 0x11110000`
  unchanged, `[-] kernel writes not observed` (rc=1 — expected
  post-fix), write() returns 8 with CAM_PATH_INVALID copied back to
  the daemon CCB (proper API rejection).
- mode b: no SWI chain, `still alive after 500 ms`, guest healthy.
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.
