DragonFlyBSD Kernel Audit
DF-1088 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/cam/scsi/scsi_pass.c b/sys/bus/cam/scsi/scsi_pass.c
index 0000000..1111111 100644
--- a/sys/bus/cam/scsi/scsi_pass.c
+++ b/sys/bus/cam/scsi/scsi_pass.c
@@ -552,6 +552,37 @@
 	ccb->ccb_h.cbfcnp = passdone;
 
 	/*
+	 * The user is not allowed to set these flags.  CAM_DATA_PHYS /
+	 * CAM_CDB_PHYS / CAM_SENSE_PHYS tell the SIM/HBA that the
+	 * associated data/CDB/sense pointer is a *physical* address; if a
+	 * user-supplied CCB is allowed through with one of them set,
+	 * cam_periph_mapmem() is bypassed and the attacker-controlled
+	 * pointer is forwarded verbatim to the SIM as a DMA target --
+	 * yielding an arbitrary physical-memory read/write primitive.
+	 * The kernel-internal XPT path already rejects CAM_DATA_PHYS for
+	 * XPT_DEV_MATCH (cam_xpt.c); the pass driver must do the same for
+	 * every user-originated CCB.  CAM_CDB_POINTER / CAM_SENSE_PTR let
+	 * the user redirect the kernel dereference through an arbitrary
+	 * pointer field and are equally kernel-only.  Reject all of them.
+	 * DF-1088.
+	 */
+	if (ccb->ccb_h.flags & (CAM_DATA_PHYS | CAM_CDB_PHYS |
+	    CAM_SENSE_PHYS | CAM_CDB_POINTER | CAM_SENSE_PTR))
+		return (EINVAL);
+
+	/*
+	 * For inline CDBs the payload lives in csio.cdb_bytes[IOCDBLEN==16].
+	 * Without this clamp a user-supplied cdb_len up to 255 makes the
+	 * SIM/HBA read past the 16-byte array into adjacent csio/union ccb
+	 * fields (including kernel pointers).  DF-1088.
+	 */
+	if (ccb->ccb_h.func_code == XPT_SCSI_IO ||
+	    ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+		if (ccb->csio.cdb_len > IOCDBLEN)
+			return (EINVAL);
+	}
+
+	/*
 	 * We only attempt to map the user memory into kernel space
 	 * if they haven't passed in a physical memory pointer,
 	 * and if there is actually an I/O operation to perform.
@@ -562,10 +593,9 @@
 	 * without data are a reasonably common occurance (e.g. test unit
 	 * ready), it will save a few cycles if we check for it here.
 	 */
-	if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0)
-	 && (((ccb->ccb_h.func_code == XPT_SCSI_IO)
+	if (((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))) {
+	  || (ccb->ccb_h.func_code == XPT_DEV_MATCH)) {
 
 		bzero(&mapinfo, sizeof(mapinfo));