]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev/scsi-id: check for invalid header from kernel buffer
authorLuca Boccassi <luca.boccassi@gmail.com>
Fri, 10 Apr 2026 18:35:59 +0000 (19:35 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Sun, 12 Apr 2026 20:28:48 +0000 (21:28 +0100)
src/udev/scsi_id/scsi_serial.c

index 7de99992578501b8f91ff103d4bc316d4c2271ea..23c3b7d786b0149f5224f4c06b23eea7ae8e06c5 100644 (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)) {