DF-1029 / fix.diff
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); |