]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lscpu: fix out-of-bounds read in parse_dmi_table
authoraizu-m <aizumusheer2@gmail.com>
Thu, 18 Jun 2026 07:59:31 +0000 (13:29 +0530)
committeraizu-m <aizumusheer2@gmail.com>
Thu, 18 Jun 2026 07:59:45 +0000 (13:29 +0530)
parse_dmi_table() walks the SMBIOS structures from the firmware DMI
table. The loop only checks that the 4-byte header fits (data + 4 <=
buf + len) and that h.length >= 4. It then reads the type 0/1/4 fields
at fixed offsets up to data[0x28] and hands the string-number bytes to
dmi_string(), which walks the structure's string set. So a truncated
final structure, where h.length, a field offset or the string set runs
past the end of the table, is read out of bounds; the bytes end up in
the CPU model/vendor strings lscpu prints. The table comes from
/sys/firmware/dmi/tables, or from /dev/mem when probing a hypervisor.

Gate each field read on h.length and stop once a structure (formatted
area plus its string set) no longer fits in the buffer, the way
dmidecode bounds these reads.

Signed-off-by: aizu-m <aizumusheer2@gmail.com>
sys-utils/lscpu-dmi.c

index a04cbb5677910144081d50f6d10e3837a1424ac4..1b50a4e54c4410aeca0730473ad7c4d4a2dd24af 100644 (file)
@@ -62,27 +62,48 @@ int parse_dmi_table(uint16_t len, uint16_t num,
                while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
                        next++;
                next += 2;
+
+               /*
+                * Stop if the structure (formatted area plus its string set)
+                * does not fit in the buffer. h.length and the field offsets
+                * below come from the firmware, so reading them by offset past
+                * the end would be an out-of-bounds read.
+                */
+               if (next - buf > len)
+                       goto done;
+
                switch (h.type) {
                case 0:
-                       di->vendor = dmi_string(&h, data[0x04]);
+                       if (h.length > 0x04)
+                               di->vendor = dmi_string(&h, data[0x04]);
                        break;
                case 1:
-                       di->manufacturer = dmi_string(&h, data[0x04]);
-                       di->product = dmi_string(&h, data[0x05]);
+                       if (h.length > 0x04)
+                               di->manufacturer = dmi_string(&h, data[0x04]);
+                       if (h.length > 0x05)
+                               di->product = dmi_string(&h, data[0x05]);
                        break;
                case 4:
                        /* Get the first processor information */
                        if (di->sockets == 0) {
-                               di->processor_manufacturer = dmi_string(&h, data[0x7]);
-                               di->processor_version = dmi_string(&h, data[0x10]);
-                               di->current_speed = *((uint16_t *)(&data[0x16]));
-                               di->max_speed = *((uint16_t *)(&data[0x14]));
-                               di->part_num = dmi_string(&h, data[0x22]);
-
-                               if (data[0x6] == 0xfe)
-                                       di->processor_family = *((uint16_t *)(&data[0x28]));
-                               else
-                                       di->processor_family = data[0x6];
+                               if (h.length > 0x07)
+                                       di->processor_manufacturer = dmi_string(&h, data[0x7]);
+                               if (h.length > 0x10)
+                                       di->processor_version = dmi_string(&h, data[0x10]);
+                               if (h.length >= 0x18) {
+                                       di->current_speed = *((uint16_t *)(&data[0x16]));
+                                       di->max_speed = *((uint16_t *)(&data[0x14]));
+                               }
+                               if (h.length > 0x22)
+                                       di->part_num = dmi_string(&h, data[0x22]);
+
+                               if (h.length > 0x06) {
+                                       if (data[0x6] == 0xfe) {
+                                               if (h.length >= 0x2a)
+                                                       di->processor_family = *((uint16_t *)(&data[0x28]));
+                                       } else
+                                               di->processor_family = data[0x6];
+                               }
                        }
                        di->sockets++;
                        break;