# DF-1113 — VERDICT

**Verdict: NOT REPRODUCED at runtime (latent — boot-time-only panic;
guest's BOCHS firmware supplies well-formed tables and boots cleanly).
Bug confirmed by source trace.**

## Mechanism (source-confirmed)

`sdt_sdth_map()` at `sys/platform/pc64/acpica/acpi_sdt.c:137-151`:

```c
void *
sdt_sdth_map(vm_paddr_t paddr)
{
    ACPI_TABLE_HEADER *sdth;
    vm_size_t mapsz;

    sdth = pmap_mapdev(paddr, sizeof(*sdth));     /* map header */
    mapsz = sdth->Length;                          /* :144 — firmware uint32 */
    pmap_unmapdev((vm_offset_t)sdth, sizeof(*sdth));

    if (mapsz < sizeof(*sdth))                     /* :147 — lower bound only */
        return NULL;

    return pmap_mapdev(paddr, mapsz);              /* :150 — unbounded */
}
```

The only validation is `mapsz < sizeof(*sdth)` — a *lower* bound of
36 bytes.  There is **no upper bound**.  `mapsz` is the firmware-supplied
`uint32 Length` of an ACPI table header.

`pmap_mapdev_attr` at `sys/platform/pc64/x86_64/pmap.c:6191-6222` does:

```c
size = roundup(offset + size, PAGE_SIZE);
va = kmem_alloc_nofault(kernel_map, size, VM_SUBSYS_MAPDEV, PAGE_SIZE);
if (va == 0)
    panic("pmap_mapdev: Couldn't alloc kernel virtual memory");
```

A `Length` near `UINT32_MAX` makes `kmem_alloc_nofault` try to allocate
~4 GiB of kernel virtual memory.  On a 4 GiB-RAM guest (the audit
config), this fails deterministically → panic.

`sdt_search_xsdt` / `sdt_search_rsdt` (acpi_sdt.c:159-279) compute
`nent` from the same unbounded `Length` (lines 194-195 / 255-256), so a
"fat" XSDT/RSDT also drives a per-entry loop calling `sdt_sdth_map` on
attacker-controlled physical addresses — every iteration can panic.

`sdt_sdth_map` runs from `sdt_probe` (`SYSINIT SI_BOOT2_PRESMP`,
acpi_sdt.c:118) — **boot-only**, before userland starts.  Attacker
controls the tables via:
- malicious / buggy firmware,
- `loader` hint `hint.acpi.0.rsdp=<phys>` pointing at a crafted RSDP
  (loader.conf override),
- a malicious hypervisor controlling guest ACPI.

## Why it can't be triggered on this guest

The guest's BOCHS firmware supplies well-formed ACPI tables (visible in
dmesg: `RSDT ... 000034`, `DSDT ... 001AF8`, `APIC ... 0000A0`, etc. —
all reasonable sizes).  The kernel boots cleanly to multi-user.

The bug fires only at boot, only with malformed firmware, only if the
guest is rebooted under malicious firmware or with a `loader.conf`
override pointing `hint.acpi.0.rsdp=` at a crafted physical address.
Re-booting the audit guest under malicious firmware is outside the
runtime threat model (it would invalidate every other PoC running on the
guest).

## Exploit chain

**Primitive class:** DoS (deterministic boot panic).  There is no
write primitive — `pmap_mapdev_attr` panics inside `kmem_alloc_nofault`
before any mapping is established, so no kernel memory is corrupted.
The realistic attacker is firmware/malicious hypervisor, not a runtime
process.

**Valid hard blocker** ("vulnerable code path is dead code at runtime on
this guest AND no harness can exercise it without rebooting under
malicious firmware").  Documented as latent.  No `uid=0` achievable.

## Fix

`fix.diff` adds an upper-bound check `mapsz > SDT_MAX_TABLE_SIZE` (1 MB)
after the existing lower-bound check, returning NULL for any table
claiming to be larger than 1 MB.  Legitimate ACPI tables are well under
1 MB (the DSDT on this guest is 0x1AF8 bytes), so the clamp rejects only
attacker-controlled / malformed lengths near UINT32_MAX.  Validated as
applies + compiles in a clean GENERIC kernel build.

## Fix validation

`not_testable` — path is boot-only and not reproducible on a guest with
honest firmware.  Validated `fix.diff` applies cleanly and compiles with
`-Werror` in a full `make nativekernel KERNCONF=X86_64_GENERIC`
(acpi_sdt.o rebuilt fresh, no warnings/errors — see
`../DF-1096/all_fixes_build.log`).

## PoC changes

- Wrote `df1113.c` (documentation-only stub explaining the source trace).
- Wrote `fix.diff`, `build.sh`, `run.sh`.
