# DF-3983 VERDICT — sbp_orb_pointer error path: xpt_done without dequeue/disown/timeout-stop

**Status: inconclusive (hardware-gated — no FireWire controller on the
audit guest, see ../DF-3981/env.txt). Source-level certainty: certain.**
No runtime attempt possible: the path requires sbp to be attached to a
live FireWire bus with a hostile peer flooding the async queues.

## Trace

Arm:
- XPT_SCSI_IO → `sbp_get_ocb` → `bus_dmamap_load` → `sbp_execute_ocb`
  (sbp.c:2497) → `sbp_enqueue_ocb` puts the OCB on `sdev->ocbs`
  (sbp.c:2637-2638) and **arms the CCB timeout callout** (sbp.c:2640-2643,
  `sbp_timeout`) → `sbp_orb_pointer(sdev, ocb)` (sbp.c:2550-2553 or 2602).

Fail:
- `sbp_orb_pointer` (sbp.c:1145-1180): `fw_asyreq` != 0 → frees only the
  xfer; sets `ocb->ccb->ccb_h.status` and **`xpt_done(ocb->ccb)`**
  (sbp.c:1177-1178) while the OCB is still (1) linked on `sdev->ocbs`,
  (2) has `ocb->ccb` set, (3) has its CCB timeout callout armed,
  (4) has a loaded data dmamap.

Attacker influence on `fw_asyreq` failure (firewire.c:182-253):
- `!(xferq->queued < xferq->maxq)` → `EINVAL` (firewire.c:230-235):
  async-response/queue saturation — sustained by any hostile 1394 node via
  split-transaction/ack-busy pressure;
- `fw_get_tlabel == -1` → `EIO` (firewire.c:240-242): the 64 tlabels are
  bus-global and trivially held by a hostile peer (also the subject of
  known DF-1021: M_WAITOK inside crit on the same path).

Double-completion arms (either one fires later):
1. **Timeout arm**: `sbp_timeout` (sbp.c:2148-2182) fires on the still-armed
   callout → case 1 → `sbp_abort_all_ocbs` (sbp.c:2164) walks the queue the
   OCB is still on → `sbp_abort_ocb` (sbp.c:2684-2710):
   `ocb->ccb != NULL` (stale, CAM already completed it at sbp.c:1178 and
   may have freed the ccb) → `callout_stop` + `xpt_done(ocb->ccb)`
   **again** (sbp.c:2705-2707) + `sbp_free_ocb`. Double xpt_done of a
   completed/freed CCB → CAM UAF/panic.
2. **Device arm**: the target device eventually answers the ORB (it may
   have received it before the tlabel/queue failure, or answers a retry):
   `sbp_recv1` → `sbp_dequeue_ocb` matches the still-queued OCB by address
   (sbp.c:2584-2599) → recv1 completes the CCB a second time
   (sbp.c:1748-1775) and `sbp_free_ocb` (sbp.c:1782); the timeout arm then
   frees it again → the same OCB node is inserted into `free_ocbs` twice →
   STAILQ corruption → two subsequent `sbp_get_ocb` calls return the same
   OCB for two different CCBs (cross-ccb status/data corruption, further
   double-dones).

Additionally the loaded dmamap is never unloaded on this path (leak of the
mapping/references held by bus_dmamap_load for the CCB data buffer).

## Severity rationale
Requires an attached SBP device + hostile-bus conditions (or even benign
congestion); impact is kernel memory corruption (double completion /
free-list duplication). Medium under the audit rubric (device-gated local
corruption), consistent with DF-2515/DF-2516 ratings.

## Fix validation
Not possible on this guest (no FireWire hardware; fw_asyreq failure cannot
be induced without a bus). fix.diff routes the error branch through
dequeue + `sbp_abort_ocb`, which performs exactly-once: dmamap
sync/unload, `callout_stop_async`, `xpt_done`, `sbp_free_ocb`.

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