# DF-5326 — `mp_bootaddress()` integer underflow corrupts the kernel physical memory map

## Verdict: **defect certain at code level; arithmetic reproduced on the audited guest; end-to-end kernel-boot manifestation untested** (boot-time trigger requires control of the loader-provided BIOS SMAP, which the standard QEMU/SeaBIOS guest cannot express)

## 1. Root cause

`sys/platform/pc64/x86_64/mp_machdep.c:206-219`:

```c
u_int
mp_bootaddress(u_int basemem)
{
	bootMP_size = mptramp_end - mptramp_start;
	boot_address = trunc_page(basemem * 1024); /* round down to 4k boundary */
	if (((basemem * 1024) - boot_address) < bootMP_size)
		boot_address -= PAGE_SIZE;	/* not enough, lower by 4k */
	/* 3 levels of page table pages */
	mptramp_pagetables = boot_address - (PAGE_SIZE * 3);

	return mptramp_pagetables;
}
```

`basemem` is the size in KB of the **first usable memory region** reported by the
firmware, laundered through the boot chain:

* `sys/platform/pc64/x86_64/machdep.c:2270` — `base_memory = physmap[1] / 1024;`
  where `physmap[]` is built at `machdep.c:2256-2260` from the loader-supplied
  `MODINFOMD_SMAP` (BIOS INT15 E820) or EFI memory map (`add_smap_entries()` /
  `add_efi_map_entries()`).
* `sys/platform/pc64/x86_64/machdep.c:2272` — `physmap[1] = mp_bootaddress(base_memory);`

There is **no lower-bound validation** on `basemem` anywhere on this path. For a
degenerate first usable region the unsigned 32-bit arithmetic wraps:

* `basemem <= 12` KB → `mptramp_pagetables = boot_address - 0x3000` wraps to
  `0xFFFFD000-0xFFFFC000`, and that wrapped value is written **into
  `physmap[1]`** — the kernel now believes the first usable RAM region is
  `[0, ~0xFFFFC000)`, i.e. the *entire low 4GB including all PCI/MMIO/ROM holes*
  is RAM. `Maxmem`/physmem accounting (`machdep.c:2285+`), vm_page probing and
  early allocations are then derived from a bogus map → kernel memory
  corruption / unbootable system.
* `basemem` 13–16 KB → `mptramp_pagetables == 0`: the three AP-initial page
  table pages written by `start_all_aps()` (`mp_machdep.c:414-438`, `pt4/pt3/pt2`)
  land **on the real-mode IVT/BDA at PA 0**, and the warm-reset vector written at
  `mp_machdep.c:446` (`WARMBOOT_OFF == KERNBASE+0x467`) is written *inside* the
  `pt4` page — mutual corruption, AP startup undefined.
* Secondary (theoretical, needs a ≥4TB first region): `basemem = physmap[1]/1024`
  truncates to `u_int` and `basemem * 1024` itself can wrap in `u_int`.

## 2. Reproduction (arithmetic level, on the audited guest)

`mp_bootaddr_underflow.c` copies the function verbatim (u_int semantics
preserved) and uses the **real** `bootMP_size` from the running audited kernel
(`nm /boot/kernel/kernel`: `mptramp_start=0xffffffff8113b8c0`,
`mptramp_end=0xffffffff8113b96a` → 170 bytes). Built and executed on the guest
with the stock `cc 8.3 [DragonFly]`. Output (`run.log`):

```
basemem(KB)  boot_address  mptramp_pagetables   effect on physmap[1]
         0   0xfffff000      0xffffc000        WRAPPED: physmap[1] ~= 4GB -> whole low 4GB marked RAM
         1   0x00000000      0xffffd000        WRAPPED ...
        ...
        12   0x00002000      0xfffff000        WRAPPED ...
        13   0x00003000      0x00000000        ZERO: trampoline PTs smash IVT/BDA at PA 0
        ...
        16   0x00003000      0x00000000        ZERO ...
        17   0x00004000      0x00001000        sane
       638   0x0009f000      0x0009c000        sane   <- real SeaBIOS value on this guest
```

The sane row (638 KB) matches the classic trampoline placement
(0x9F000 / PTs at 0x9C000), confirming the reproducer models the live code path.

## 3. Why the kernel-boot manifestation was not exercised

Triggering the real code path requires the loader to hand the kernel a SMAP
whose first usable region ends below ~17 KB. On the audit guest:

* the SMAP is generated by SeaBIOS at loader boot time (fixed 0–639KB layout);
  QEMU exposes no interface to shrink the first usable region below 640 KB
  (`etc/e820` fw_cfg only *adds* reserved entries);
* the SMAP blob is collected by the loader from the BIOS at loader runtime —
  it is not read from any file an in-guest attacker can write;
* marking low memory reserved via fw_cfg moves the first *usable* region
  start, not its end — `physmap[1]` stays ≈639 KB, so `basemem` stays sane.

So the only realistic carriers of the trigger are malicious/compromised
firmware or a tampered boot chain (boot area / loader binary), which matches
the Low severity and the same threat class as the accepted DF-1034.

## 4. Impact ceiling

Boot-time kernel memory-map corruption from crafted firmware/boot chain:
unbootable machine at best; at worst a physmem map that hands out MMIO/ROM
pages as RAM (silent memory corruption). No runtime (post-boot), unprivileged,
or remote reachability — the function runs exactly once, early in `init386()`.

## 5. Fix

`fix.diff` (authored against the read-only `sys/` tree, never applied there):
fail fast on a degenerate `basemem` instead of silently wrapping — consistent
with the tree's existing boot-time panics on malformed firmware tables (e.g.
`madt_lapic_pass1()` panics on a failed MADT walk). Not kernel-build-validated
(finding is Low / boot-time / firmware-gated; fix validation reserved for
memory-corruption findings that reproduce).

## 6. Artifacts

* `mp_bootaddr_underflow.c` — verbatim-logic reproducer (guest-compiled)
* `build.sh` / `run.sh` — exact commands
* `run.log` — full guest build+run output (BUILD_OK + table)
* `env.txt` — guest uname, dmesg SMP path, kernel symbols, cc version
* `fix.diff` — proposed fix

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