# DF-1155 — heap overflow in `isp_handle_platform_atio` CDB copy

## Verdict
**NOT REPRODUCED** on this guest (defense-in-depth source bug confirmed;
path unreachable on the default GENERIC DragonFlyBSD kernel running in
QEMU).  Real source-level defect; fix.diff authored.

## Mechanism (source trace, bug confirmed real)
In `sys/dev/disk/isp/isp_freebsd.c:1829`, `isp_handle_platform_atio()`
copies a SCSI CDB delivered by the QLogic firmware into a CAM
`ccb_accept_tio`:

```c
/* sys/dev/disk/isp/isp_freebsd.c:1925-1927 */
atiop->init_id  = GET_IID_VAL(aep->at_iid);
atiop->cdb_len  = aep->at_cdblen;                                          /* L1926 */
ISP_MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen);          /* L1927 */
```

The sizes are mismatched and there is **no clamp**:

| Object | Field | Size | Source |
|---|---|---|---|
| source `at_entry_t.at_cdb` | `aep->at_cdb` | `ATIO_CDBLEN = 26` | `sys/dev/disk/isp/ispmbox.h:1965,1980` |
| length `at_entry_t.at_cdblen` | `aep->at_cdblen` | `uint8_t` ⇒ 0..255 | `sys/dev/disk/isp/ispmbox.h:1973` |
| destination `ccb_accept_tio.cdb_io.cdb_bytes` | `atiop->cdb_io.cdb_bytes` | `IOCDBLEN = CAM_MAX_CDBLEN = 16` | `sys/bus/cam/cam.h:53`, `sys/bus/cam/cam_ccb.h:44` |

The firmware-reported `at_cdblen` (0..255) is fed directly to `ISP_MEMCPY`
without clamping to the destination size.  A malicious firmware (or hostile
device on a parallel SCSI bus, or a SCSI-passthrough VM) reports
`at_cdblen > 16` ⇒ the copy overruns `cdb_bytes[16]` into the subsequent
fields of `struct ccb_accept_tio` (`cdb_len`, `tag_action`, `sense_len`,
`tag_id`, `init_id`, …) and beyond into adjacent slab memory.  Per the
finding, the FC siblings `isp_handle_platform_atio2` (`:1953`) and
`isp_handle_platform_atio7` (`:2126`) use the fixed sizes
`ATIO2_CDBLEN = 16` / `sizeof(fcp_cmnd_cdb) = 16`, so they are not
affected.

## Why it does NOT reproduce on this guest
This finding has **three independent reachability blockers**, any one of
which is fatal:

### 1. The vulnerable function is not in the kernel binary.
`isp_handle_platform_atio` is defined at `isp_freebsd.c:1828-1950`, which
sits **outside** any `#ifdef ISP_TARGET_MODE` block.  However **every
caller** is inside such a block — the only entry point is the
`RQSTYPE_ATIO` dispatch at `isp_freebsd.c:5354-5359` inside the
`ISPASYNC_TARGET_ACTION` case at `:5334`, which is inside the
`#ifdef ISP_TARGET_MODE` block at `:5263` (closed at `:5431`).  Since
`ISP_TARGET_MODE` is **not defined** by `X86_64_GENERIC`:
```
$ grep ISP_TARGET_MODE sys/config/X86_64_GENERIC
(empty)
```
the function has no callers, the compiler/linker drops it, and:
```
$ ssh dfbsd 'nm /boot/kernel/kernel.debug | grep -c isp_handle_platform_atio'
0
```
⇒ confirmed: zero `isp_handle_platform_atio` symbols in the running kernel.

### 2. `ISP_TARGET_MODE` is opt-in, not default.
`sys/conf/options:203` declares `ISP_TARGET_MODE opt_isp.h`.  The only
configuration that mentions it is `sys/config/LINT64:1462` and it is
**commented out** (`#options ISP_TARGET_MODE=1`).  So even a custom
kernel must explicitly opt in; default GENERIC and most production kernels
do not enable target mode.

### 3. No parallel-SCSI QLogic HBA in the QEMU guest.
Even with ISP_TARGET_MODE enabled, the ISP driver only registers target
mode for actual QLogic SCSI HBAs (1040/1080/12160/2100/2200/2300/2322
parallel SCSI; 2400/2500 FC).  The QEMU guest has **none** of these on
its virtual PCI bus — `devinfo -v | grep -i qlogic` is empty, and no
`isp` controller attaches at probe.  No firmware request queue → no ATIO
entry → no path to the vulnerable function.

## Exploit chain
none — primitive (kernel heap overflow) requires all three of: kernel
built with `ISP_TARGET_MODE`, a QLogic SCSI HBA physically present (or
SCSI-passthrough VM), and malicious firmware/device.  None of these hold
on the default GENERIC DragonFlyBSD guest.

## PoC changes
none — no executable PoC is possible.  Even building a custom
`ISP_TARGET_MODE=1` kernel would not help: the guest has no QLogic HBA,
so the ISP driver would never call `isp_handle_platform_atio`.  The
function's only caller is the firmware-dispatch `ISPASYNC_TARGET_ACTION`
path, which fires only when the HBA's request queue delivers an
`RQSTYPE_ATIO` entry.

## Recommended fix
Clamp the copy length to the destination buffer size (16 bytes); also
clamp the `cdb_len` recorded for the upper layer to the same value.  See
`fix.diff` — applies cleanly with `git apply` (validated).  Matches the
finding markdown's proposal.

The sibling FC paths (`isp_handle_platform_atio2`/`atio7`) are already
safe because they use the fixed `ATIO2_CDBLEN = 16` for both source and
destination, so no parallel fix is needed there.
