DragonFlyBSD Kernel Audit
DF-2414 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/disk/advansys/advlib.c b/sys/dev/disk/advansys/advlib.c
--- a/sys/dev/disk/advansys/advlib.c
+++ b/sys/dev/disk/advansys/advlib.c
@@ -1004,6 +1004,16 @@
 		 */
 		cinfo_index =
 		    adv_read_lram_32(adv, halt_q_addr + ADV_SCSIQ_D_CINFO_IDX);
+		/* cinfo_index is a 32-bit value read from RISC LRAM (firmware-
+		 * shared memory); validate it against the ccb_infos[] allocation
+		 * (max_openings entries) before indexing, or a firmware bug /
+		 * malicious device can drive an OOB read of adv->ccb_infos.
+		 * See DF-2414. */
+		if (cinfo_index >= adv->max_openings) {
+			device_printf(adv->dev,
+			    "CHK_CONDITION: bad cinfo_index %u\n", cinfo_index);
+			return;
+		}
 		ccb = adv->ccb_infos[cinfo_index].ccb;
 		xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
 		ccb->ccb_h.status |= CAM_DEV_QFRZN;
@@ -1045,6 +1055,12 @@
 					      + ADV_SCSIQ_SCSI_STATUS);
 		cinfo_index =
 		    adv_read_lram_32(adv, halt_q_addr + ADV_SCSIQ_D_CINFO_IDX);
+		/* See DF-2414: validate firmware-supplied ccb index. */
+		if (cinfo_index >= adv->max_openings) {
+			device_printf(adv->dev,
+			    "QUEUE_FULL: bad cinfo_index %u\n", cinfo_index);
+			return;
+		}
 		ccb = adv->ccb_infos[cinfo_index].ccb;
 		xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
 		ccb->ccb_h.status |= CAM_DEV_QFRZN|CAM_SCSI_STATUS_ERROR;
@@ -1892,6 +1908,14 @@
 
 		cinfo_index =
 		    adv_read_lram_32(adv, halt_q_addr + ADV_SCSIQ_D_CINFO_IDX);
+		/* See DF-2414: validate firmware-supplied ccb index before
+		 * indexing ccb_infos[] (and before the adv_set_syncrate(... ccb)
+		 * below derefs it). */
+		if (cinfo_index >= adv->max_openings) {
+			device_printf(adv->dev,
+			    "SDTR: bad cinfo_index %u\n", cinfo_index);
+			return;
+		}
 		ccb = adv->ccb_infos[cinfo_index].ccb;
 		tinfo = &adv->tinfo[tid_no];
 		sdtr_accept = TRUE;
@@ -1985,6 +2009,14 @@
 		q_addr = ADV_QNO_TO_QADDR(q_no);
 
 		adv_copy_lram_doneq(adv, q_addr, scsiq, adv->max_dma_count);
+		/* scsiq->d2.ccb_index is copied from firmware LRAM and indexes
+		 * ccb_infos[max_openings]; validate before dereferencing.
+		 * See DF-2414. */
+		if (scsiq->d2.ccb_index >= adv->max_openings) {
+			device_printf(adv->dev,
+			    "abort: bad ccb_index %u\n", scsiq->d2.ccb_index);
+			continue;
+		}
 		ccb_info = &adv->ccb_infos[scsiq->d2.ccb_index];
 		if (((scsiq->q_status & QS_READY) != 0)
 		 && ((scsiq->q_status & QS_ABORTED) == 0)