ACPI: processor: validate MADT IOAPIC entry bounds

The IOAPIC hotplug lookup parses both MADT and _MAT records directly.
The MADT walk previously used a subtable's declared length to advance
the cursor after only locating a generic header.  The _MAT path likewise
passed a generic header to the IOAPIC helper.

Validate that a current record has a complete generic header, that its
declared length is contained in the available record range, and that a
typed IOAPIC record contains the full fixed IOAPIC body before reading
its fields.  Use the same relation for both MADT and _MAT provider
paths.

Fixes: ecf5636dcd ("ACPI: Add interfaces to parse IOAPIC ID for IOAPIC hotplug")
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
Link: https://patch.msgid.link/20260715083253.22831-1-pengpeng@iscas.ac.cn
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Pengpeng Hou
2026-07-15 16:32:53 +08:00
committed by Rafael J. Wysocki
parent 1590cf0329
commit 2c50ffdc73

View File

@@ -336,11 +336,26 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
EXPORT_SYMBOL_GPL(acpi_get_cpuid);
#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC
static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base,
static bool madt_entry_is_valid(struct acpi_subtable_header *entry,
unsigned long end)
{
unsigned long start = (unsigned long)entry;
if (start >= end || end - start < sizeof(*entry))
return false;
return entry->length >= sizeof(*entry) && entry->length <= end - start;
}
static int get_ioapic_id(struct acpi_subtable_header *entry,
const unsigned long end, u32 gsi_base,
u64 *phys_addr, int *ioapic_id)
{
struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry;
if (!madt_entry_is_valid(entry, end) || BAD_MADT_ENTRY(ioapic, end))
return 0;
if (ioapic->global_irq_base != gsi_base)
return 0;
@@ -361,17 +376,19 @@ static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr)
return apic_id;
entry = (unsigned long)madt;
if (madt->header.length < sizeof(*madt))
return apic_id;
madt_end = entry + madt->header.length;
/* Parse all entries looking for a match. */
entry += sizeof(struct acpi_table_madt);
while (entry + sizeof(struct acpi_subtable_header) < madt_end) {
while (madt_entry_is_valid((struct acpi_subtable_header *)entry,
madt_end)) {
hdr = (struct acpi_subtable_header *)entry;
if (hdr->type == ACPI_MADT_TYPE_IO_APIC &&
get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id))
get_ioapic_id(hdr, madt_end, gsi_base, phys_addr, &apic_id))
break;
else
entry += hdr->length;
entry += hdr->length;
}
return apic_id;
@@ -398,7 +415,9 @@ static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base,
header = (struct acpi_subtable_header *)obj->buffer.pointer;
if (header->type == ACPI_MADT_TYPE_IO_APIC)
get_ioapic_id(header, gsi_base, phys_addr, &apic_id);
get_ioapic_id(header,
(unsigned long)header + obj->buffer.length,
gsi_base, phys_addr, &apic_id);
exit:
kfree(buffer.pointer);