# DF-1088 — No validation/stripping of dangerous user CCB flags in CAM pass

## Verdict

**REPRODUCED (privilege-gated runtime).** The bug exists verbatim in
`sys/bus/cam/scsi/scsi_pass.c:565-568`; a root-originated `CAMIOCOMMAND`
with `CAM_DIR_IN | CAM_DATA_PHYS` and `csio.cdb_len = 255` is accepted by
the unpatched kernel (rc=0, three-of-three deterministic runs) — i.e. the
dangerous CCB is forwarded to the SIM with the user's `csio.data_ptr`
interpreted as a physical address and the CDB read length un-clamped.
After the `fix.diff` is applied the same CCB returns `EINVAL`.

The trigger is **gated behind `caps_priv_check_self(SYSCAP_RESTRICTEDROOT)`**
(`scsi_pass.c:308`) plus a default mode-0600 devfs node, so an
unprivileged user cannot reach the bug on a default install. The finding's
realistic impact (jail escape / securelevel bypass on hosts without IOMMU)
requires a jail granted `RESTRICTEDROOT` with `/dev/passN` exposed, or an
already-capable process. The `passsendccb` defect itself is real, confirmed
deterministically, and closed by the fix.

## Mechanism (confirmed by source trace and runtime)

`passsendccb` merges the user CCB into a kernel CCB via `xpt_merge_ccb`
(`scsi_pass.c:546`) and then conditionally maps user buffers:

```c
/* scsi_pass.c:565-568 — the bug */
if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
    && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE))
  || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) {
    ...
    error = cam_periph_mapmem(ccb, &mapinfo);
    ...
}
```

When the user sets `CAM_DATA_PHYS` (`cam_ccb.h:84`, `0x00200000`):
- The gate is false → `cam_periph_mapmem` is **skipped entirely**.
- `cam_periph_runccb` (`scsi_pass.c:600`) still forwards the CCB through
  `xpt_action` to the SIM/HBA.
- The SIM sees `csio.data_ptr` (`cam_ccb.h:598`) as a **physical address**
  (the flag's documented meaning) and DMAs to/from it. For `CAM_DIR_IN` the
  device response overwrites the attacker-chosen physical address.

The kernel-internal XPT path already knows this is unsafe — `cam_xpt.c:1133`
rejects `CAM_DATA_PHYS` for `XPT_DEV_MATCH`. The pass driver does not.

Two related defects sit on the same function:

1. **No `cdb_len` clamp.** For inline CDBs (`cdb_bytes[IOCDBLEN]`,
   `cam_ccb.h:587`, `IOCDBLEN = CAM_MAX_CDBLEN = 16`, `cam.h:53`), a
   `cdb_len` of 255 makes the SIM read 255 bytes from the 16-byte array,
   over-reading adjacent `csio`/`union ccb` fields including kernel
   pointers (`msg_ptr`, `data_ptr`). The same class of pointer-vs-array
   confusion affects `CAM_CDB_POINTER`, `CAM_SENSE_PTR`, `CAM_SENSE_PHYS`,
   `CAM_CDB_PHYS` — none of which the pass driver inspects.

2. **No privilege-boundary flag rejection.** `CAM_DATA_PHYS`,
   `CAM_CDB_PHYS`, `CAM_SENSE_PHYS` are kernel-internal flags (set by
   kernel-allocated CCBs whose buffers the kernel already DMA-mapped).
   They have no legitimate meaning in a user-supplied CCB. Their presence
   is therefore always a request to bypass `cam_periph_mapmem` — and
   should be rejected.

## Reproduction

```
$ ssh dfbsd-maxx 'cd poc/DF-1088 && ./df1088_trigger'      # as maxx (uid 1001)
[unpriv] open /dev/pass0: Permission denied (errno=13)
[unpriv] pass0 access denied at devfs (mode 0600) -- DF-1088 trigger is privilege-gated
[unpriv] re-run as root to exercise the CCB-shape signature via CAMIOCOMMAND

# as root (the only context that can open the device)
$ ./dfbsd-qemu/vm.sh run_root '/root/df1088_trigger'
[unpriv] open /dev/pass0: SUCCEEDED (fd=3) -- unexpected
[root] CAMIOCOMMAND CAM_DATA_PHYS|cdb_len=255: rc=0 errno=0 (Undefined error: 0)
[root] BUG: kernel forwarded attacker physical pointer to SIM without rejection (DF-1088 present)
```

The rc=0 (CCB forwarded) is the bug signature. The PoC was run three
times — all three returned rc=0 with no rejection.

## Fix

`fix.diff` (git-apply-able) adds two guards to `passsendccb` immediately
after `xpt_merge_ccb`:

1. Reject any user CCB carrying `CAM_DATA_PHYS | CAM_CDB_PHYS |
   CAM_SENSE_PHYS | CAM_CDB_POINTER | CAM_SENSE_PTR` (all kernel-internal
   flags) with `EINVAL`.
2. For `XPT_SCSI_IO`/`XPT_CONT_TARGET_IO`, reject `csio.cdb_len > IOCDBLEN`.

The pre-existing `CAM_DATA_PHYS == 0` test on the `cam_periph_mapmem` gate
is now dead (we rejected the flag above) and is removed for clarity.

This mirrors the kernel-internal XPT pattern at `cam_xpt.c:1133` and the
upstream FreeBSD fix that adds similar flag rejections to its `passsendccb`.

## Hardware / guest notes

- `/dev/pass0` is present on the audit guest (attached to QEMU DVD-ROM via
  `atapci0`/`cd0`). No special hardware required to demonstrate the CCB
  flag-shape bug — `passsendccb` is reached for any open of the device.
- The DMA-to-arbitrary-physical-memory impact chain requires (a) an
  attacker-chosen physical `data_ptr` AND (b) a SCSI target that completes
  the I/O. (b) is present in the guest (the DVD-ROM), but the bug's
  real-world value is in a jail/securelevel context where a privileged
  capability is grantable and the device is exposed. The trigger PoC stops
  short of completing an actual DMA to a chosen physical address (no IOMMU
  bypass demonstration), but proves the kernel forwards the malicious CCB
  — which is the precise claim of the finding.
