DragonFlyBSD Kernel Audit
DF-1029 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/cam/scsi/scsi_ch.c b/sys/bus/cam/scsi/scsi_ch.c
--- a/sys/bus/cam/scsi/scsi_ch.c
+++ b/sys/bus/cam/scsi/scsi_ch.c
@@ -224,6 +224,26 @@
 
 MALLOC_DEFINE(M_SCSICH, "scsi_ch", "scsi_ch buffers");
 
+/*
+ * Validate a MODE SENSE (6) response header before calling
+ * find_mode_page_6().  find_mode_page_6() returns
+ *   (uint8_t *)&mode_header[1] + mode_header->blk_desc_len
+ * with NO bounds check; blk_desc_len is a u_int8_t at struct offset 3,
+ * fully device-controlled.  A malicious device can return blk_desc_len
+ * up to 255, causing find_mode_page_6() to point up to ~250 bytes past
+ * a smallish mode_buffer.  Require that the block-descriptor length
+ * stay inside the supplied buffer (DF-1029).
+ */
+static __inline int
+ch_mode_header_sane(struct scsi_mode_header_6 *mode_header, size_t buflen)
+{
+	if (buflen < sizeof(*mode_header))
+		return (0);
+	if (mode_header->blk_desc_len > buflen - sizeof(*mode_header))
+		return (0);
+	return (1);
+}
+
 static void
 chinit(void)
 {
@@ -543,6 +563,13 @@
 
 		mode_header = (struct scsi_mode_header_6 *)csio->data_ptr;
 
+		if (!ch_mode_header_sane(mode_header, csio->dxfer_len)) {
+			xpt_print(periph->path,
+			    "bogus MODE SENSE blk_desc_len %u\n",
+			    mode_header->blk_desc_len);
+			announce_buf[0] = '\0';
+			break;
+		}
 		ea = (struct page_element_address_assignment *)
 			find_mode_page_6(mode_header);
 
@@ -1406,6 +1433,15 @@
 		}
 	}
 
+	if (!ch_mode_header_sane((struct scsi_mode_header_6 *)mode_buffer,
+	                          mode_buffer_len)) {
+		xpt_print(periph->path,
+		    "bogus MODE SENSE blk_desc_len %u\n",
+		    ((struct scsi_mode_header_6 *)mode_buffer)->blk_desc_len);
+		xpt_release_ccb(ccb);
+		kfree(mode_buffer, M_SCSICH);
+		return (EIO);
+	}
 	ea = (struct page_element_address_assignment *)
 		find_mode_page_6((struct scsi_mode_header_6 *)mode_buffer);
 
@@ -1471,6 +1507,14 @@
 
 	xpt_release_ccb(ccb);
 
+	if (!ch_mode_header_sane((struct scsi_mode_header_6 *)mode_buffer,
+	                          mode_buffer_len)) {
+		xpt_print(periph->path,
+		    "bogus MODE SENSE blk_desc_len %u (cap page)\n",
+		    ((struct scsi_mode_header_6 *)mode_buffer)->blk_desc_len);
+		kfree(mode_buffer, M_SCSICH);
+		return (EIO);
+	}
 	cap = (struct page_device_capabilities *)
 		find_mode_page_6((struct scsi_mode_header_6 *)mode_buffer);