udev/scsi-id: check for invalid header from kernel buffer

This commit is contained in:
Luca Boccassi
2026-04-12 21:28:48 +01:00
parent 5f700d148c
commit 06d3f37336
+27 -6
View File
@@ -542,7 +542,9 @@ static int check_fill_0x83_prespc3(struct scsi_id_device *dev_scsi,
/* serial has been memset to zero before */
j = strlen(serial); /* j = 1; */
for (i = 0; (i < page_83[3]) && (j < max_len-3); ++i) {
/* Cap reported page length to buffer size in case of malformed responses */
int page_len = MIN((int)page_83[3], SCSI_INQ_BUFF_LEN - 4);
for (i = 0; (i < page_len) && (j < max_len-3); ++i) {
serial[j++] = hexchar(page_83[4+i] >> 4);
serial[j++] = hexchar(page_83[4+i]);
}
@@ -610,12 +612,25 @@ static int do_scsi_page83_inquiry(struct scsi_id_device *dev_scsi, int fd,
* Search for a match in the prioritized id_search_list - since WWN ids
* come first we can pick up the WWN in check_fill_0x83_id().
*/
/* Cap reported page length to buffer size in case of malformed responses.
* Below, j can equal page_end, and at that point page_83[j + 3] (the first descriptor data byte)
* must still be readable before the inner bounds check, so page_end + 4 < SCSI_INQ_BUFF_LEN
* requires page_end <= SCSI_INQ_BUFF_LEN - 5. */
unsigned page_end = MIN(((unsigned)page_83[2] << 8) + (unsigned)page_83[3] + 3U,
(unsigned)SCSI_INQ_BUFF_LEN - 5U);
FOREACH_ELEMENT(search_value, id_search_list) {
/*
* Examine each descriptor returned. There is normally only
* one or a small number of descriptors.
*/
for (unsigned j = 4; j <= ((unsigned)page_83[2] << 8) + (unsigned)page_83[3] + 3; j += page_83[j + 3] + 4) {
for (unsigned j = 4; j <= page_end; j += page_83[j + 3] + 4) {
/* Ensure the full descriptor fits within the buffer, including
* fixed-offset accesses up to page_83[7] in the TGTGROUP path
* of check_fill_0x83_id(), so require at least 8 bytes from j */
if (j + MAX(4U + (unsigned)page_83[j + 3], 8U) > (unsigned)SCSI_INQ_BUFF_LEN)
break;
retval = check_fill_0x83_id(dev_scsi, page_83 + j,
search_value,
serial, serial_short, len,
@@ -688,7 +703,9 @@ static int do_scsi_page83_prespc3_inquiry(struct scsi_id_device *dev_scsi, int f
* using two bytes of ASCII for each byte
* in the page_83.
*/
while (i < (page_83[3]+4)) {
/* Cap reported page length to buffer size in case of malformed responses */
int page_len = MIN((int)page_83[3] + 4, SCSI_INQ_BUFF_LEN);
while (i < page_len && j + 2 < len) {
serial[j++] = hexchar(page_83[i] >> 4);
serial[j++] = hexchar(page_83[i]);
i++;
@@ -725,7 +742,8 @@ static int do_scsi_page80_inquiry(struct scsi_id_device *dev_scsi, int fd,
* Prepend 'S' to avoid unlikely collision with page 0x83 vendor
* specific type where we prepend '0' + vendor + model.
*/
len = buf[3];
/* Cap reported page length to buffer size in case of malformed responses */
len = MIN((int)buf[3], SCSI_INQ_BUFF_LEN - 4);
if (serial) {
serial[0] = 'S';
ser_ind = append_vendor_model(dev_scsi, serial + 1);
@@ -860,7 +878,10 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname,
goto completed;
}
for (ind = 4; ind <= page0[3] + 3; ind++)
/* Cap reported page length to buffer size in case of malformed responses */
int page0_end = MIN((int)page0[3] + 3, SCSI_INQ_BUFF_LEN - 1);
for (ind = 4; ind <= page0_end; ind++)
if (page0[ind] == PAGE_83)
if (!do_scsi_page83_inquiry(dev_scsi, fd,
dev_scsi->serial, dev_scsi->serial_short, len, dev_scsi->unit_serial_number, dev_scsi->wwn, dev_scsi->wwn_vendor_extension, dev_scsi->tgpt_group)) {
@@ -871,7 +892,7 @@ int scsi_get_serial(struct scsi_id_device *dev_scsi, const char *devname,
goto completed;
}
for (ind = 4; ind <= page0[3] + 3; ind++)
for (ind = 4; ind <= page0_end; ind++)
if (page0[ind] == PAGE_80)
if (!do_scsi_page80_inquiry(dev_scsi, fd,
dev_scsi->serial, dev_scsi->serial_short, len)) {