# Verdict — DF-5536 (REPRODUCED)

## Finding
The dump walk trusts kernel page-table frames at panic time:

- `minidump_machdep.c:260` — `dump_add_page(pdp[i] & PG_FRAME)` (the PD-page frame) and
  `:281` — `dump_add_page(pd[j] & PG_FRAME)` (the PT-page frame) are **not** guarded by
  `is_dumpable()` (unlike the leaf-PFN paths at `:271-272` and `:291-292`).
- `dump_add_page()` (:452-460) / `dump_drop_page()` (:463-471) perform
  `atomic_set_long(&vm_page_dump[idx], 1ul << bit)` with `idx = pa >> 18` and **no bounds
  check** against `vm_page_dump_size` (bitmap covers only physical RAM, vm/vm_page.c:398-403).
- `:263`/`:285` then do `PHYS_TO_DMAP(frame)` reads of the same unvalidated frames.

With a single corrupted PDP/PD entry (which is precisely the state a successful
kernel-memory-corruption exploit leaves behind, or the residue of the very bug that caused
the panic), `idx` is attacker-chosen: the write offset is `frame >> 15` bytes past
`vm_page_dump`, for any 52-bit frame — an uncontrolled OOB kernel write (single bit-OR,
8-byte aligned) or a kernel page fault, both *during* the crash-dump path.

## Reproduction (baseline, fresh guest, X86_64_GENERIC 6.5-DEV)
KLD `mddiag.ko` plants `pdp[64] = <frame>|V` in an unused KVA slot (nothing live is
disturbed), then panics. Two variants:

1. frame = 2^46 (`panic.txt`): write target `vm_page_dump[2^28] = 0xfffff800c0000000`
   (bitmap+2 GB, mapped kernel memory) → **silent bit-OR into a live kernel object** →
   nested `panic: assertion "obj != NULL" failed in vm_object_hold_shared` → guest wedged,
   no dump.
2. frame = 2^47 (`panic.2` — direct attribution): predicted target
   `vm_page_dump[2^29] = 0xfffff80140000000` (bitmap+4 GB, unused KVA slot):
   ```
   Fatal trap 12: page fault while in kernel mode
   fault virtual address = 0xfffff80140000000     <- exactly the predicted vm_page_dump[idx]
   fault code        = supervisor write data, page not present   <- the atomic_set_long
   panic: page fault
   ```
   The fault VA matches the rig's printed `&vm_page_dump[frame>>18]` byte-for-byte and the
   fault is a *supervisor write* — i.e. the `atomic_set_long` in `dump_add_page` called
   unguarded from walk-1 line 260. No "Dump complete"; guest wedged (no reset).

## Impact
Not a privilege boundary: the precondition is kernel page-table corruption (attacker is
already ring-0-equivalent, or the machine is already crashing from a memory-safety bug).
Consequence is loss of the crash dump exactly when it matters (evidence destroyed /
availability of the dump path): the dump writer performs an uncontrolled kernel write or
double-panics instead of writing the core. Severity: Low (panic-path robustness / hardening);
the write primitive itself is real and demonstrated.

## Fix
`fix.diff` (three layers):
1. `is_dumpable(pdp[i] & PG_FRAME)` guard before :260 (skip the whole slot);
2. `is_dumpable(pd[j] & PG_FRAME)` guard before :281 (skip the 2MB block — also removes the
   `PHYS_TO_DMAP` read of the bogus frame at :285);
3. bounds check `pa < (vm_page_dump_size/8)*64` in `dump_add_page`/`dump_drop_page`
   (defense in depth; also fixes the theoretical `int idx` truncation for pa >= 2^49).

Validated on a patched kernel: same rig → walk skips the planted entry → "Dump complete",
normal reboot, savecore extracts the core.

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