# DF-5650 — SRAT walk lacks entry-extent validation: OOB read past the mapped table (console info leak + early-boot kernel page fault)

## What

`sys/platform/pc64/acpica/acpi_srat.c` walks SRAT subtables checking only
`(char *)mem < (char *)srat + srat->Header.Length` (`:94`) — i.e. merely
that the **first byte** of the entry header is inside the table. It never
checks that the entry fits (`entry->Length` vs remaining bytes) nor that the
entry is long enough for the structure whose fields it then reads:

- `mem->mem.Flags` (`:101`, packed u32@+28 of the entry),
- `mem->mem.BaseAddress/Length/ProximityDomain` (`:104-107`, +8/+16/+2),
- inner scan `cpu->cpu.Flags/ProximityDomainLo/ApicId` (`:121-135`).

A crafted MEMORY-affinity entry header (`Type=1`) parked at the last two
bytes of the table (declared `Length` ends right after the header) makes the
kernel read and **kprintf** firmware-adjacent physical memory up to 40 bytes
beyond the table's declared end — bytes that were never part of the table —
to the console/dmesg. When the table's declared length ends exactly on a
page boundary, the same read crosses past the `pmap_mapdev` mapping
(`sdt_sdth_map`, `acpi_sdt.c:138-151`, maps exactly
`roundup(Length, PAGE_SIZE)`, fresh KVA per `pmap.c:6192-6217`) and takes a
kernel page fault during early boot (`Fatal trap 12`).

The MADT sibling validates all of this (`madt_iterate_entries`,
`acpi_madt.c:151-167`); the SRAT parser has none of it.

## Rig

Same planted-ACPI chain as DF-5649 (loader kenv `hint.acpi.0.rsdp` +
QEMU `-device loader`). Read `DECODE_LOG.md` (in ../DF-5649/) first —
ACPICA SRAT structs are `#pragma pack(1)` (`actbl3.h:194`), so the packed
offsets are Prox@2 / Base@8 / Length@16 / Flags@28 (proven empirically;
the faulting instruction is the flags gate itself:
`testb $0x1,0x1c(%r12)`).

## Build

    python3 gen_srat.py leak3 /tmp/srat_l3.bin    # console-leak flavor
    python3 gen_srat.py fault3 /tmp/srat_f3.bin   # page-fault flavor

## Run

    ./run.sh leak3     # or fault3 (see run.sh; hint must be in loader.conf)

## Expected output

leak3 (see run.log) — every printed value comes from bytes planted
**past the declared table end** (table Length=66, entry header at +64,
markers at +66..+85):

    MemAffinity deadbeefcafe0123,1177411667286MB Prox=1397899604 (not found)
    vm_page: avg 893 pages per queue, 1020 queues

fault3 (see run.fault.log) — table declared length ends on the 0x40001000
page boundary; the Flags read at entry+28 faults on unmapped KVA:

    Fatal trap 12: page fault while in kernel mode
    ...
    Stopped at  -0x7f3de188:  testb   $0x1,0x1c(%r12)
    db>

## Fix validation

With `fix.diff` (shared with DF-5649) built into the guest kernel, both
bloots boot cleanly past `SI_BOOT2_NUMA`:

    srat_probe: invalid SRAT entry len 48     <- entry rejected, never read
    vm_page: avg 893 pages per queue, 1020 queues

No marker disclosure (`fixed_l3.log`), no trap (`fixed_f3.log`).
