Unbounded csio->cdb_len bcopy into the 32-byte MPI2 CDB field overflows the request frame in the req_frames DMA pool
Summary
mprsas_action_scsiio copies csio->cdb_len bytes (u_int8_t, up to 255) into req->CDB.CDB32[32] with no bound when CAM_CDB_POINTER is set, and only an INVARIANTS-only KASSERT otherwise. A cdb_len of 255 overwrites the frame's SGL area and ~2 adjacent request frames; for the last SMID it runs past the whole DMA allocation with attacker-chosen bytes; the pointer path also reads a raw user cdb_ptr (cam_periph_mapmem never touches it). Root-only (/dev/pass* is root-only): privileged CAM client corrupts kernel DMA memory. ahci's ATAPI path validates 6..16 - the bound is SIM-dependent and mpr lacks one; same class as DF-2422. GUEST RUN PROVED the framework-level precondition (cdb_len=255 + CAM_CDB_POINTER passes CAM untouched, status 0x6 = ahci's own rejection); the mpr manifestation needs an mpr-attached bus, absent on this guest.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3805 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 305 B | β raw | |
| VERDICT.md | β | 3.5 KB | β raw | |
| cdb_overlong.c | β | 2.6 KB | view raw | |
| build.sh | β | 102 B | view raw | |
| run.sh | β | 109 B | view raw | |
| build.log | β | 9 B | view raw | |
| run.log | β | 179 B | view raw | |
| env.txt | β | 855 B | view raw | |
| fix.diff | β | 978 B | view raw | |
| verdict.json | β | 2.7 KB | view raw | |
| manifest.json | β | 692 B | view raw |
DF-3805 evidence pack
Unbounded csio->cdb_len bcopy into the 32-byte MPI2 CDB field
(mprsas_action_scsiio). cdb_overlong.c proves on the guest that the CAM
framework passes an oversized CDB straight to the SIM. Read VERDICT.md.
verdict.json: not_reproduced (no mpr SIM on guest; root-gated).
DF-3805 β Unbounded csio->cdb_len bcopy overflows the 32-byte MPI2 CDB field
- File:
sys/dev/raid/mpr/mpr_sas.c - Class: kernel memory corruption (root-gated: requires CAM passthrough,
/dev/pass*is root-only) - Severity: Low / Confidence: certain (mechanics)
- Status on guest: not_reproduced β CAM framework imposes no bound (demonstrated on guest); the mpr SIM itself could not be reached (no SAS3 HBA)
Defect
mprsas_action_scsiio copies the CDB with the caller-supplied length and no
bound of its own (sys/dev/raid/mpr/mpr_sas.c:2102-2109):
if (csio->ccb_h.flags & CAM_CDB_POINTER)
bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len);
else {
KASSERT(csio->cdb_len <= IOCDBLEN, ...); /* INVARIANTS-only */
bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0], csio->cdb_len);
}
CDB32is 32 bytes (sys/dev/raid/mpr/mpi/mpi2_init.h:124, offset 0x40,SGLat 0x60 follows in the same frame).cdb_lenisu_int8_t(sys/bus/cam/cam_ccb.h:603) β up to 255.- With
CAM_CDB_POINTERthere is no check at all; without it, the only check is aKASSERT, which is INVARIANTS-only in DragonFly (sys/sys/systm.h:94-118) β compiled out in production kernels. For the non-pointer path the source over-readscdb_bytes[16]inside/past the CCB. - Nothing in the CAM framework bounds
cdb_len:passioctl/passsendccb(sys/bus/cam/scsi/scsi_pass.c:462-516, 531-615) merges the user CCB viaxpt_merge_ccbwith no validation, andcam_periph_mapmemnever handlescdb_ptr(grep: no reference insys/bus/cam/cam_periph.c) β the SIM also dereferences a raw user pointer withoutcopyin.
A cdb_len of e.g. 255 copies 255 bytes into the request frame
(cm->cm_req = sc->req_frames + i * sc->reqframesz, sys/dev/raid/mpr/mpr.c:1530),
overwriting the frame's SGL area and ~2 subsequent request frames; for the last
SMID (i = num_reqs-1) it runs past the end of the req_frames DMA allocation
with fully attacker-chosen bytes.
Contrast: ahci's ATAPI path validates cdb_len 6..16 and returns
CAM_CCB_LEN_ERR (sys/dev/disk/ahci/ahci_cam.c:1512-1516) β the bound is
SIM-dependent and mpr is missing one. (Same class as DF-2422 against ahci.)
Trigger / PoC
cdb_overlong.c submits cdb_len=255 + CAM_CDB_POINTER via CAMIOCOMMAND:
build.sh: cc -O -o /tmp/cdb_overlong /root/cdb_overlong.c -> BUILD_OK (build.log) run.sh: /tmp/cdb_overlong /dev/pass0 cdb_len=255 CCB accepted by CAM framework; SIM status = 0x6 (CAM_CCB_LEN_ERR) (run.log)
This proves the framework-level fact on the guest: an oversized CDB reaches
the SIM layer untouched (the status 0x6 is the ahci SIM's own rejection).
On this guest the mpr SIM is absent (pciconf -l: no SAS3 HBA; camcontrol
devlist: only ata buses), so the overflowing bcopy itself could not be
exercised β not_reproduced for the mpr manifestation. The in-SIM reachability
of the mpr path additionally requires root (/dev/pass* is root-only), which
caps severity at Low.
Fix
Bound the copy like ahci does (see fix.diff): reject cdb_len outside 6..32
with CAM_CCB_LEN_ERR, and copy through a kernel buffer for the pointer path.
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.
Fix verification
not_testablefix.diff adds the range check; the mpr SIM cannot be reached on this guest, so no kernel build/validation was performed.
findings/poc/DF-3805/fix.diff
Confirmed kernel references
Detail
Exploit chain
root user issues CAMIOCOMMAND with cdb_len>32 -> bcopy overflows request frame in req_frames DMA pool with attacker bytes; on the last SMID writes past the DMA allocation
Evidence (decisive lines)
cdb_overlong.c, build.log (BUILD_OK), run.log (status 0x6 = CAM_CCB_LEN_ERR from ahci SIM; framework imposed no bound), env.txt, VERDICT.md, fix.diff
PoC changes
Added the includes DF needs (bus/cam/scsi/scsi_message.h for MSG_SIMPLE_Q_TAG, bus/cam/scsi/scsi_pass.h for CAMIOCOMMAND); DF libcam is a stub so the probe uses the raw CAMIOCOMMAND ioctl on /dev/pass0.
Verified recommended fix
Reject cdb_len outside 6..32 with CAM_CCB_LEN_ERR before the bcopy in mprsas_action_scsiio.
Verdict
mprsas_action_scsiio copies csio->cdb_len bytes (u8, up to 255) into the 32-byte req->CDB.CDB32 field (mpr_sas.c:2102-2109; CDB32 at mpi2_init.h:124, offset 0x40) with no bound when CAM_CDB_POINTER is set and only an INVARIANTS-only KASSERT otherwise (sys/sys/systm.h:94-118). Guest run PROVED the framework-level precondition: a cdb_len=255 CAM_CDB_POINTER CCB submitted via CAMIOCOMMAND passes passioctl/xpt_merge_ccb untouched and reaches the SIM (status 0x6 = CAM_CCB_LEN_ERR came from the ahci SIM's own 6..16 check, ahci_cam.c:1512; run.log). The mpr manifestation (overflow of the request frame in the req_frames DMA pool, mpr.c:1530, incl. past the allocation for the last SMID; also a kernel read of a raw user cdb_ptr since cam_periph_mapmem never maps it) could not be exercised because the guest has no SAS3 HBA and the mpr SIM never attaches (env.txt). Root-gated (/dev/pass*), capping severity at Low.
No comments yet.