# DF-5341 — `mptable_map()` trusts firmware table pointers with no signature / checksum / PA-range validation

## Verdict: **defect certain at code level; acceptance table proven on the audited guest; kernel-boot manifestation untested** (needs injected firmware table memory)

## 1. Root cause

The floating-pointer search matches **only the 4-byte `_MP_` signature**
(`mptable.c:359, 375`). Everything after that is trust:

`mptable_map()` — `sys/platform/pc64/x86_64/mptable.c:501-539`:

```c
fps = pmap_mapdev(mptable_fps_phyaddr, sizeof(*fps));
if (fps->pap != 0) {					/* mptable.c:512 — the ONLY check */
	/*
	 * Map configuration table header to get the base table size
	 */
	cth = pmap_mapdev(fps->pap, sizeof(*cth));	/* mptable.c:517 — arbitrary PA read */
	cth_mapsz = cth->base_table_length;		/* mptable.c:518 — u_short from that PA */
	pmap_unmapdev((vm_offset_t)cth, sizeof(*cth));
	if (cth_mapsz < sizeof(*cth)) { ... EINVAL }
	cth = pmap_mapdev(fps->pap, cth_mapsz);		/* mptable.c:531 — up to 65535 bytes */
}
```

Never validated anywhere in the file (verified by grep of every field use):

* MPFPS checksum byte (`fps->checksum`, struct at `mptable.c:93`) — 0 reads
* MPFPS `spec_rev` / `length` sanity — 0 reads
* MPCTH `"PCMP"` signature (`cth->signature`, struct at `mptable.c:103`) —
  **0 reads**
* MPCTH checksum (`cth->checksum`, `mptable.c:106`) — 0 reads
* `pap` / `pap + base_table_length` inside mapped RAM — no range check exists

`pmap_mapdev_attr()` (`pmap.c:6192-6218`) installs PTEs for ANY physical
address with no bounds check, so:

* every accepted `pap` (any nonzero u_int32) becomes an early-boot kernel
  **read of an arbitrary physical address** (MMIO holes, PCI space, ROM),
* `pap + cth_mapsz` can cross the 4GB wrap (e.g. `pap=0xFFFFFF00`,
  `len=0x1000` → span `[0xFFFFFF00, 0x100000F00)`),
* the parsed garbage then drives CPU/IOAPIC enumeration; its failure modes
  are the sibling findings' KKASSERT panics (DF-5339) and mis-enumerated
  topology (DF-5342/DF-5343), or benign EINVAL.

Because neither structure's signature nor checksum is verified, a single
stray 4-byte-aligned `_MP_` dword anywhere in the scanned 1 KB EBDA / 128 KB
BIOS regions (530 KB of address space total) promotes arbitrary memory into a
"MP floating pointer" whose `pap` field is then followed blindly.

Historical note: FreeBSD's mptable code this file descends from verified the
MPFPS checksum and the `PCMP` signature before use; the DragonFly port dropped
all of it.

## 2. Reproduction (decision table, on the audited guest)

`pap_trust.c` prints the acceptance predicate (`pap != 0`) and the mapdev
span math. `run.log` (guest `cc 8.3`):

```
  pap=0xfebc0000 (MMIO hole          ) -> ACCEPTED, mapped + parsed as MP config table
  pap=0xa0000000 (PCI MMIO           ) -> ACCEPTED, mapped + parsed as MP config table
  pap=0xffffff00 (top of 4GB - 0x100 ) -> ACCEPTED, mapped + parsed as MP config table
  pap=0xffffffff (last byte of 4G    ) -> ACCEPTED, mapped + parsed as MP config table
  pap=0xdeadbeef (total garbage      ) -> ACCEPTED, mapped + parsed as MP config table
  ...
  4GB-crossing   pap=0xffffff00 len=4096  map span [0x0ffffff00..0x100000f00)  ** CROSSES THE 4GB WRAP **
```

## 3. Threat model / why kernel-boot manifestation is untested

The pointer and table live in firmware-provided memory; only malicious or
compromised firmware/boot-loader controls them. The standard QEMU/SeaBIOS
guest generates its own (absent) MP tables and offers no injection path
(`-fw_cfg` cannot place `_MP_` structures into the scanned regions), and
`mptable_probe` runs exactly once at `SI_BOOT2_PRESMP` — no runtime, no
unprivileged reach. Consequence ceiling on this class: wrong/absent SMP
enumeration, arbitrary-PA reads, or the sibling findings' boot panics.

## 4. Recommended fix

`fix.diff` — restore the two cheapest, highest-value checks from the MP
spec / FreeBSD heritage before adopting `pap`:

1. MPFPS checksum over `sizeof(struct MPFPS)` must be 0;
2. the mapped config-table header must start with `"PCMP"`.

(A full fix would also checksum the MPCTH and range-check `pap`/`pap+len`
against the physmap.)

## 5. Fix validation

`git apply --check` passes. A patched-kernel A/B boot test is not
constructible on this guest (no table injection), hence
`fix_status: not_testable`.

## 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.
