From: aizu-m Date: Thu, 18 Jun 2026 07:59:31 +0000 (+0530) Subject: lscpu: fix out-of-bounds read in parse_dmi_table X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a96936e5e5e2f606853fb01e38e20ef8a2a4a4a1;p=thirdparty%2Futil-linux.git lscpu: fix out-of-bounds read in parse_dmi_table 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 --- diff --git a/sys-utils/lscpu-dmi.c b/sys-utils/lscpu-dmi.c index a04cbb567..1b50a4e54 100644 --- a/sys-utils/lscpu-dmi.c +++ b/sys-utils/lscpu-dmi.c @@ -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;