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));