⬢ DragonFlyBSD Kernel Audit
DF-5271 / fix.diff
← back to finding ↓ download raw
--- a/sys/bus/cam/scsi/scsi_cd.c	2026-10-01 22:57:28.582365539 +0000
+++ b/sys/bus/cam/scsi/scsi_cd.c	2026-10-01 22:57:28.594365386 +0000
@@ -3436,6 +3436,7 @@
 	 */
 	if (error == 0) {
 		u_int32_t data_len;
+		u_int32_t bd_len, hdr_size;
 
 		if (data->cdb_size == 10) {
 			struct scsi_mode_header_10 *hdr10;
@@ -3443,12 +3444,16 @@
 			hdr10 = (struct scsi_mode_header_10 *)data->mode_buf;
 			data_len = scsi_2btoul(hdr10->data_length);
 			data_len += sizeof(hdr10->data_length);
+			bd_len = scsi_2btoul(hdr10->blk_desc_len);
+			hdr_size = sizeof(*hdr10);
 		} else {
 			struct scsi_mode_header_6 *hdr6;
 
 			hdr6 = (struct scsi_mode_header_6 *)data->mode_buf;
 			data_len = hdr6->data_length;
 			data_len += sizeof(hdr6->data_length);
+			bd_len = hdr6->blk_desc_len;
+			hdr_size = sizeof(*hdr6);
 		}
 
 		/*
@@ -3458,7 +3463,18 @@
 		 * drive returns multiple block descriptors, or if it sets
 		 * the data length incorrectly.
 		 */
-		if (data_len > data->alloc_len) {
+		/*
+		 * The block descriptor length is likewise device
+		 * controlled and feeds find_mode_page_*(), which derives
+		 * the page pointer that the ioctl layer then reads and
+		 * writes.  Make sure header + block descriptors + the
+		 * mode page the caller touches stays inside the
+		 * allocation.
+		 */
+		if (data_len > data->alloc_len ||
+		    bd_len > data->alloc_len ||
+		    hdr_size + bd_len + sizeof(union cd_pages) >
+		    data->alloc_len) {
 			xpt_print(periph->path, "allocated modepage %d length "
 			    "%d < returned length %d\n", page, data->alloc_len,
 			    data_len);