# DF-5668 — AcpiOsGetRootPointer() accepts hint.acpi.0.rsdp with zero validation: second kenv RSDP consumer feeding ACPICA (boot panic at AcpiTbLoadNamespace+0x36)

## What

`sys/platform/pc64/acpica/OsdEnvironment.c:72-73` — `AcpiOsGetRootPointer()`'s
kenv branch caches whatever u_long the loader passes in `hint.acpi.0.rsdp`
into `acpi_root_phys` and returns it to ACPICA — no address bound, no
`"RSD PTR "` signature, no checksum, no Revision sanity.  This is the
**second, independent acceptance** of the same hint (the native walk's
acceptance at `acpi_sdt.c:71` is DF-5588; that fix does not cure this one).

Downstream, unvalidated:

* `AcpiInitializeTables` (tbxface.c:257) → `AcpiTbParseRootTable`
  (tbutils.c:376) maps the RSDP and reads `Revision` /
  `Xsdt/RsdtPhysicalAddress` directly — `AcpiTbValidateRsdp`
  (tbxfroot.c:211-243, sig+checksum) only runs on the *scan* path
  (`AcpiFindRootPointer`), never on the host-supplied-pointer path;
* `acpi_probe` (acpi.c:462-478) derefs the same cache (Revision, XSDT/RSDT
  paddr, RSDT header) with no checks;
* `acpi_attach` → `AcpiLoadTables` (acpi.c:553) → `AcpiTbLoadNamespace`
  (tbxfload.c:275-285) computes `&Tables[AcpiGbl_DsdtIndex]` and derefs
  `Table->Signature` — with a FADT-less crafted chain `AcpiGbl_DsdtIndex`
  is still `ACPI_INVALID_TABLE_INDEX` (0xFFFFFFFF, acglobal.h:171; only
  `AcpiTbParseFadt` tbfadt.c:490 ever sets it) → OOB read →
  `Fatal trap 12 ... Stopped at AcpiTbLoadNamespace+0x36:
  cmpl $0x54445344,0x14(%rbx)` (the 'DSDT' compare).

## Rig (proven planted-ACPI family rig)

loader.conf `hint.acpi.0.rsdp=0x40000000` + QEMU
`-device loader,file=<blob>,addr=0x40000000,force-raw=on` planting an
RSDP→RSDT→MADT chain (BSP+2AP+real-KVM-IOAPIC control MADT — DF-5588's
proven content, so the native walk survives and boot reaches acpi0 attach).

## Build

    ./build.sh     # gen_chain.py -> nosig_fadtless.bin, valid_fadtless.bin

## Run (each phase boots the guest; see run.sh)

* **S1** (stock kernel, invalid-sig chain): native walk consumes crafted MADT
  (`lapic_count=3`), `acpi0: <DFBSD >` (crafted RSDT OEM id read by
  acpi_probe), then `Fatal trap 12 ... AcpiTbLoadNamespace+0x36` —
  run.S1_nosig.boot.log, panic.txt.
* **S2** (stock kernel, FULLY valid sig+checksums, FADT-less): identical
  fatal trap — a deliberate boot-owner gets past any RSDP-level validation;
  the ACPICA-side index guard is needed too — run.S2_validfadtless.boot.log.
* **S3** (hint → zeroed RAM, no blob): stock sdt wedges pre-SMP
  (`RSDT paddr == 0`, serial frozen) — run.S3_zeroed_wedge.boot.log; on a
  DF-5588-patched kernel the wedge moves into ACPICA's walk (observed in
  DF-5588 fix_run.log tail: fault at AcpiTbLoadNamespace+0x36 / late-boot
  freeze) — cured here in F3.
* **F1** (fix.diff, invalid-sig chain): `AcpiOsGetRootPointer: kenv RSDP
  failed validation, falling back to firmware scan` → firmware RSDP
  0xF59A0 → `acpi0: <BOCHS BXPC> [attached!]`, machdep.acpi_root=0xF59A0,
  boot completes, NO panic — fixrun.F1_nosig.boot.log.
* **F2** (fix.diff, fully-valid chain): RSDP validation passes (chain is
  genuinely valid), contrib guard catches the FADT-less walk →
  `acpi0: Could not enable ACPI: AE_NO_ACPI_TABLES` (graceful,
  AcpiLoadTables swallows the load failure at tbxfload.c:213) → boot
  completes, NO panic — fixrun.F2_validfadtless.boot.log.
* **F3** (fix.diff + DF-5588 fix.diff, zeroed hint): BOTH acceptances
  reject → firmware tables → `lapic_count=6`, `acpi0 [attached!]`,
  clean boot — the wedge is gone end-to-end — fixrun.F3_zeroedhint.boot.log.

## Fix

`fix.diff` = (a) `OsdEnvironment.c`: bound the hinted address against
`ptoa(Maxmem)`, require `"RSD PTR "` + `Revision <= 2` + v1 checksum
(+ extended checksum for rev ≥ 2) before caching; on failure log and fall
back to `AcpiFindRootPointer` (firmware scan) in both the no-hint and
invalid-hint cases; (b) `tbxfload.c` belt-and-suspenders guard: treat
`AcpiGbl_DsdtIndex == ACPI_INVALID_TABLE_INDEX` as AE_NO_ACPI_TABLES
before indexing `Tables[]` (upstream-ACPICA hardening; reachable via any
fully-valid FADT-less chain regardless of how the root pointer was
obtained — real broken firmware included).

## Trust boundary

The hint is set by the loader (`/boot/loader.conf` or the loader prompt):
a boot-owner (root / boot-disk write / console).  No unprivileged in-guest
path reaches it: `AcpiOsGetRootPointer` is only called during cold boot
(acpi_Startup/acpi_probe) and caches its result; runtime kenv changes
cannot affect it.  Severity Medium, consistent with DF-5588/DF-1113.
