# DF-5649 VERDICT — REPRODUCED (boot-time DoS, hang)

## 1. Bottom line

A crafted SRAT with a zero-length subtable entry wedges the kernel in an
infinite loop inside `srat_probe()` (`sys/platform/pc64/acpica/acpi_srat.c:95`
outer walk, `:117` inner CPU rescan) at `SI_BOOT2_NUMA`. The boot silently
stops between the `SI_BOOT2_CPU_TOPOLOGY` messages and everything after;
there is no panic, no console output, and no way to progress. Reproduced
3 times on the stock Jul-2 with-src kernel; gone (clean rejection message,
boot proceeds) on a kernel rebuilt with `fix.diff`.

## 2. Root cause (path:line)

- `sys/platform/pc64/acpica/acpi_srat.c:93-95` — outer walk advance
  `mem = mem + mem->head.Length`; `head.Length` is a firmware-supplied
  `UINT8` with no check `Length >= sizeof(ACPI_SUBTABLE_HEADER)`.
- `sys/platform/pc64/acpica/acpi_srat.c:114-117` — inner walk, same defect.
- Sibling proof the check is known-necessary:
  `sys/platform/pc64/acpica/acpi_madt.c:156-167` (`madt_iterate_entries`)
  rejects `ent->Length < sizeof(*ent)` and `ent->Length > size - cur`.
- Injection vector: `sys/platform/pc64/acpica/acpi_sdt.c:71` (kenv RSDP hint
  accepted with no validation — DF-5588).

Threat model: firmware/loader-stage attacker (planted RSDP chain, malicious
VM host, or compromised bootloader config) — identical to the
DF-5430..5433/5583/5588 family. No unprivileged in-guest path; severity Low
per family calibration (boot-time DoS brick).

## 3. Reproduction evidence

- `run.log` (= run2_hang.log) and `run.2.log` (independent boots): serial
  output ends at `CPU Topology: cores_per_chip: 6; ...` (line 85) and never
  advances; no `vm_page: avg` line; no login. QEMU stays alive burning CPU.
- `control_baseline.log`: identical rig with a well-formed SRAT —
  `vm_page: avg 893 pages per queue, 1020 queues` (line 86, ~1s after CPU
  Topology) and the boot continues to the (rig-artifact) DSDT trap at
  AcpiTbLoadNamespace.
- `wedge_page.bin`: physical memory dump taken via the QEMU monitor while
  wedged. Bytes at +0x430: `00 10 00 00 01 00 00 00 ...` (cpu affinity
  entry, Length=16) and at +0x440: `00 00 ...` — the crafted zero-length
  entry the loop is spinning on:
  ```
  000430 00 10 00 00 01 00 00 00 00 00 00 00 00 00 00 00
  000440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  ```
- Trigger table (`gen_srat.py` scenario `hang`): SRAT Length=66 =
  48-byte header + 16-byte cpu entry + 2-byte `{Type=0, Length=0}`.

## 4. Fix validation (baseline vs patched)

`fix.diff` applied inside the guest (`/usr/src/sys/platform/pc64/acpica/
acpi_srat.c`, md5 c289f1c2bbea4ab3369eace0ae0c72ff), kernel rebuilt with
`make -j6 nativekernel KERNCONF=X86_64_GENERIC` (BUILD_OK), installed, same
blob re-run:

| | baseline (stock) | patched |
|---|---|---|
| serial after "CPU Topology" | nothing, forever | `srat_probe: invalid SRAT entry len 0` |
| `vm_page: avg` line | never | line 87 |
| boot proceeds past SI_BOOT2_NUMA | no | yes (to the rig-artifact DSDT trap, same as control) |

(`fixed_hang.log` lines 85-87 and 352.)

Note: the `Fatal trap 12` at line ~352 of the patched run is
`AcpiTbLoadNamespace` tripping on the DSDT-less planted table chain — a rig
artifact present in EVERY boot with this rig (including the healthy control,
`control_baseline.log` line 351); it occurs long after `SI_BOOT2_NUMA` and
is unrelated to the SRAT fix.

## 5. Rig hardening notes (see DECODE_LOG.md)

- The blob must live at a physical address stable through
  `SI_BOOT2_NUMA`; 0x40000000 worked 10+/10 times, the SMAP-reserved hole
  0xbffdd000 is zeroed by SeaBIOS before the loader runs.
- ACPICA SRAT structs are packed (`actbl3.h:194` `#pragma pack(1)`):
  MEM_AFFINITY = Header(2) + Prox u32@2 + Rsvd u16@6 + Base u64@8 +
  Length u64@16 + Rsvd u32@24 + Flags u32@28, size 40. Entry-crafting
  code must use these offsets (DECODE_LOG.md documents the empirical
  decode that proved it, including the faulting instruction
  `testb $0x1,0x1c(%r12)` = `mem->mem.Flags & ACPI_SRAT_MEM_ENABLED`).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
