Skip to content

numa: bound SRAT/SLIT parsing against untrusted ACPI table lengths - #1482

Open
gburd wants to merge 1 commit into
cloudius-systems:masterfrom
gburd:pr/numa-harden
Open

numa: bound SRAT/SLIT parsing against untrusted ACPI table lengths#1482
gburd wants to merge 1 commit into
cloudius-systems:masterfrom
gburd:pr/numa-harden

Conversation

@gburd

@gburd gburd commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

What

core/numa.cc parses the ACPI SRAT and SLIT tables during NUMA topology
discovery. Those tables come from firmware and should be treated as untrusted
input. Two out-of-bounds read exposures exist on a malformed or truncated table:

  1. SRAT subtable walk only checks that a subtable starts before the end of
    the table and that its Length is non-zero. A subtable whose Length runs
    past the table end (or an SRAT header shorter than the table struct itself) is
    still dereferenced via get_parent_from_member, reading past the SRAT buffer.

  2. parse_slit() validates LocalityCount against the SRAT node count, but
    then reads n*n distance bytes from Entry without checking that those bytes
    fit within the table's declared Header.Length. A truncated SLIT (or one
    whose LocalityCount matches nr_nodes but whose body is short) reads out of
    bounds, and n*n itself can overflow.

Fix

  • Walk SRAT with an explicit byte cursor and reject any subtable whose declared
    length is shorter than the subtable header or extends past the end of the
    table; also reject an SRAT header shorter than the table struct. Using a byte
    cursor also removes the reliance on non-standard void* pointer arithmetic (a
    GNU extension that happens to compile under the kernel's GCC).

  • In parse_slit(), require that offsetof(Entry) + n*n fits within
    Header.Length and guard against n*n overflow before assigning the distance
    matrix.

No functional change on well-formed firmware tables; this only rejects
malformed/truncated tables that would otherwise cause an out-of-bounds read. The
bounds arithmetic was validated with a standalone self-check covering the
overrun, short-header, and truncated-SLIT cases.

parse_srat() and parse_slit() read ACPI tables supplied by firmware, which
should be treated as untrusted input. The current walk has two out-of-bounds
read exposures on a malformed or truncated table:

- The SRAT subtable loop only checks that a subtable *starts* before the end of
  the table and that its Length is non-zero. A subtable whose Length runs past
  the table end (or a header shorter than the table struct itself) is still
  dereferenced, reading past the SRAT buffer.

- parse_slit() validates LocalityCount against the SRAT node count but then
  reads n*n distance bytes from Entry without checking that those bytes fit
  within the table's declared Header.Length. A truncated SLIT (or one whose
  LocalityCount matches nr_nodes but whose body is short) reads out of bounds,
  and n*n itself can overflow.

Harden both:

- Walk SRAT with an explicit byte cursor and reject any subtable whose declared
  length is shorter than the subtable header or extends past the end of the
  table; also reject an SRAT header shorter than the table struct. Using a byte
  cursor also drops the reliance on non-standard void* pointer arithmetic (a GNU
  extension).

- In parse_slit(), require that offsetof(Entry) + n*n fits within Header.Length
  and guard against n*n overflow before assigning the distance matrix.

No functional change on well-formed firmware tables.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant