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 @@ -1163,6 +1163,29 @@ "warning, READ ELEMENT STATUS avail != count\n"); } + /* + * Validate device-controlled lengths before using them. + * + * copy_element_status() reads a fixed + * sizeof(struct read_element_status_descriptor) byte window out of + * every descriptor slot, so a device that reports a shorter edl + * would cause an OOB read past each slot (and, on the last slot, + * past the data allocation). Likewise, the response may legitimately + * report more available elements than the user asked for; in that + * case the original loop ran past `data`, copying OOB heap bytes + * out to userspace. Clamp avail to the user-supplied count and + * reject obviously bogus descriptor lengths. + */ + if (desclen < sizeof(struct read_element_status_descriptor)) { + xpt_print(periph->path, + "bogus READ ELEMENT STATUS descriptor length %zu\n", + desclen); + error = EIO; + goto done; + } + if (avail > cesr->cesr_element_count) + avail = cesr->cesr_element_count; + user_data = (struct changer_element_status *) kmalloc(avail * sizeof(struct changer_element_status), M_DEVBUF, M_INTWAIT | M_ZERO);