# DF-4222 VERDICT — source-certain NULL back-pointer deref in XPT_ABORT; HW(vmm)-gated guest, not live-reproducible

## Status: inconclusive (no pvscsi device on audit guest); defect certain at source level

## Root cause

`pvscsi_action()` case `XPT_ABORT`:

```
sys/dev/virtual/vmware/pvscsi/pvscsi.c:1394   abort_ccb = ccb->cab.abort_ccb;
sys/dev/virtual/vmware/pvscsi/pvscsi.c:1395   abort_hcb = abort_ccb->ccb_h.ccb_pvscsi_hcb;
sys/dev/virtual/vmware/pvscsi/pvscsi.c:1397   if (abort_hcb->ccb != NULL && abort_hcb->ccb == abort_ccb) {
                                                    ^^^^^^^^^^^^^^^ NULL deref when abort_hcb == NULL
```

`ccb_pvscsi_hcb` lifecycle in this driver:

* set when a SCSI I/O starts: pvscsi.c:1326 (`ccb_h->ccb_pvscsi_hcb = hcb`);
* **cleared on every completion**: pvscsi.c:1018
  (`ccb->ccb_h.ccb_pvscsi_hcb = NULL;`) inside `pvscsi_process_completion`,
  which runs under `sc->lock` from the interrupt / cmp-ring processing;
* never set for CCBs that never passed through `pvscsi_action(XPT_SCSI_IO)`.

Therefore any `XPT_ABORT` whose `abort_ccb` (a) already completed normally or
(b) was never issued through this SIM presents `abort_hcb == NULL` and the
first operand read `abort_hcb->ccb` faults at a small fixed offset from NULL
(union ccb's `ccb_h` is at offset 0; `spriv_ptr0` sits a few hundred bytes
in) ⇒ page fault ⇒ **panic**.

The driver's own error paths make the dangling-pointer situation worse in a
benign direction but underline the inconsistency: the `bus_dmamap_load_ccb`
failure path (pvscsi.c:1200-1212) and the two `finish_ccb` paths
(pvscsi.c:1329-1341) `xpt_done()` the CCB **without** clearing
`ccb_pvscsi_hcb` (unlike pvscsi.c:1018) — leaving a stale non-NULL pointer
that happens not to crash the abort handler only because the hcb memory is
still valid.

## Race that reaches it in-tree

`targ(4)`:

```
sys/bus/cam/scsi/scsi_target.c:498-500   crit_enter(); abort_all_pending(softc); crit_exit();
sys/bus/cam/scsi/scsi_target.c:1099-1103 TAILQ_FOREACH(ccb_h, &softc->pending_ccb_queue, ...) {
                                             cab->abort_ccb = (union ccb *)ccb_h;
                                             xpt_action((union ccb *)cab);
```

`crit_enter()` blocks interrupts only on the *current* CPU. A pvscsi
completion interrupt on another CPU completes the very CCB being iterated
(`pvscsi_process_completion` clears `ccb_pvscsi_hcb` at pvscsi.c:1018 and
`targdone` removes it from the pending queue); the subsequently dispatched
`XPT_ABORT` then dereferences the now-NULL back-pointer ⇒ panic. The
user-triggered abort path (`targwrite` → `targgetccb` translation at
scsi_target.c:719-745) has the same window against the pending queue.

Impact ceiling: local denial of service (panic). Requires pvscsi hardware
(VMware host) plus a privileged or racy CAM-level abort — hence severity Low.

## Why not a duplicate of DF-1454/DF-1455

Those cover the completion ring's device-fed `sense_len` bound and
`context`/log2 validation. This is the CAM action path (`XPT_ABORT`) and a
host-side NULL check — different function, different input, different fix.
(A comparison against the FreeBSD original was attempted but the FreeBSD
pvscsi driver never shipped in a release branch and is no longer fetchable;
the defect is established from DragonFly's own source, which NULL-checks the
identical pointer in `pvscsi_timeout` at pvscsi.c:850-853 but not here.)

## Fix validation

* `fix.diff` = one-line change to the standard
  `abort_hcb != NULL && abort_hcb->ccb == abort_ccb` pattern.
* Applied with `patch -p2` to the pristine guest kernel source
  (md5-identical to host `sys/`, see `env.txt`): `patch_apply.log` — clean.
* Module rebuild: `build.log` — OK.
* Runtime HW-gated on this guest (no pvscsi device; `pciconf -l` shows no
  15ad vendor) ⇒ `fix_status: inconclusive` (compile-validated).

## Kernel references

* sys/dev/virtual/vmware/pvscsi/pvscsi.c:1389-1411 (XPT_ABORT case)
* sys/dev/virtual/vmware/pvscsi/pvscsi.c:1018-1019 (pointer cleared on completion)
* sys/dev/virtual/vmware/pvscsi/pvscsi.c:1326-1327 (pointer set at start)
* sys/dev/virtual/vmware/pvscsi/pvscsi.c:850-853 (timeout path DOES NULL-check)
* sys/dev/virtual/vmware/pvscsi/pvscsi.c:1200-1212, 1329-1341 (error paths leave stale pointer)
* sys/bus/cam/scsi/scsi_target.c:498-500, 1099-1103, 719-745 (racy issuers)
* sys/dev/virtual/virtio/scsi/virtio_scsi.c:941-974 (safe sibling pattern)

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