# DF-5802 VERDICT — REPRODUCED (panic, privileged trigger)

## What the bug is

`vm_fault_wire()` and `vm_fault_unwire()` classify "fictitious" entries with
two DIFFERENT rules:

* `sys/vm/vm_fault.c:2607-2620` (wire): a `switch (entry->maptype)` —
  `VM_MAPTYPE_UKSMAP` ⇒ `fictitious = TRUE` (case at :2614-2616). Correct:
  the UKSMAP fault path (`vm_fault()` :562-580) `pmap_enter()`s a stack
  `fakem` page and never calls `vm_page_wire()` on anything.
* `sys/vm/vm_fault.c:2679-2681` (unwire): `fictitious` computed ONLY from
  `entry->ba.object->type` (OBJT_DEVICE/OBJT_MGTDEVICE). UKSMAP entries have
  `ba.object == NULL` (set by `vm_map_lookup`, sys/vm/vm_map.c:4552-4555) ⇒
  `fictitious = FALSE`.

Consequently, after `mlock()` of a UKSMAP range (which the wire path handles
correctly), `munlock()` — or process-exit unwiring — executes
`vm_fault_unwire()` :2691-2695:

```c
m = pmap_unwire(pmap, &va);              /* pmap.c:5633: m = PHYS_TO_VM_PAGE(pa); */
vm_page_busy_wait(m, FALSE, "vmwrpg");
vm_page_unwire(m, 1);
```

on a page the wire path never wired:

1. **RAM-backed UKSMAP mappings** (`/dev/upmap`, minor 5,
   `sys/kern/kern_memio.c:726+` → `pmap_kextract` of the live per-process
   `p_upmap` kernel page): `PHYS_TO_VM_PAGE()` yields the *real* vm_page of a
   kernel heap page whose `wire_count` belongs to kmem, not to this mapping.
   `vm_page_unwire()` either panics
   (`vm_page.c:3302-3303`, `wire_count == 0`) or silently steals one wiring
   from the kernel page (`wire_count` 1→0 ⇒ `vm_page_activate()` of a live
   kernel data page — delayed-corruption face). On the stock guest this
   variant survived *visibly* — i.e. it took the silent-corruption path.

2. **`/dev/mem` at pa above Maxmem** (minor 0, `fake->phys_addr =
   ptoa(pindex)`, `sys/kern/kern_memio.c:448-452` — the mmap offset IS the
   physical address, user-chosen): `PHYS_TO_VM_PAGE(pa)` is an **unchecked
   `vm_page_array` index** (`sys/vm/vm_page.h:454-455`
   `&vm_page_array[atop(pa) - first_page]`). For pa = 0xFF000000 on the
   reference guest (v_page_count 998273, Maxmem ≈ 0xFDBC0000) the index runs
   ~4,000 entries past the end of the array; `vm_page_busy_wait()` /
   `vm_page_unwire()` then read/write kernel memory beyond `vm_page_array`.
   Observed: `panic: vm_page_unwire: invalid wire count: 0` from garbage
   `wire_count`, depending on adjacent memory the outcome is a panic, a DDB
   wedge, or an unmonitored atomic write past the array.

## Reproduction (stock kernel, guest 6.5-DEVELOPMENT #0 X86_64_GENERIC)

* Built in-guest with base cc 8.3 (`build.log`).
* `run.log`: variant `upmap` — survived munlock (silent wiring-accounting
  corruption face); variant `mem` — output truncated at `munlock()`, guest
  DOWN immediately after.
* `panic.txt` (serial console): exact causal chain
  `sys_munlock → vm_map_user_wiring → vm_fault_unwire → vm_page_unwire →
  panic "vm_page_unwire: invalid wire count: 0"`.

## Fix validation

* `fix.diff` mirrors `vm_fault_wire()`'s maptype switch in
  `vm_fault_unwire()` (`VM_MAPTYPE_UKSMAP ⇒ fictitious = TRUE`).
* Applied to the guest's `/usr/src` (`Hunk #1 succeeded at 2676`),
  `make nativekernel KERNCONF=X86_64_GENERIC` (buildkernel refused: no
  buildworld tools), `make installkernel`, reboot into `#1` kernel
  (`fix_build.log`).
* Re-ran the identical `run.sh`: **both variants print
  `SURVIVED munlock`, rc=0, guest stays up** (`run.patched.log`,
  `fix_run.log`). The panic is gone; the upmap variant's silent unwire is
  gone with it.
* Guest reset to the clean `with-src` snapshot afterwards.

## Impact ceiling — honest

Privileged trigger: `mlock(2)` is `SYSCAP_RESTRICTEDROOT`-gated on x86-64
(pc64 does not define `pmap_wired_count`, so `sys_mlock` compiles the
privilege branch, `sys/vm/vm_mmap.c:1028-1034`), and the decisive `mem`
variant additionally needs `/dev/mem` (root:kmem 0640). `/dev/upmap` is
0666, but the mlock gate still binds for it. So: root user → kernel panic /
kernel-memory write past `vm_page_array` / corruption of kmem wiring —
robustness defect, not a privilege boundary crossing. No user→root route
(class doesn't permit one from an unprivileged seat). Severity Low,
confidence certain.

## Why this is not DF-0944/2685/2686

Those are: MGTDEVICE pager-FAIL NULL page (readrest path), mlock wiring
zero-filling swapped pages (TRYPAGER wire mask), and the pager-error PIP
wakeup. This finding is the *unwire* classification asymmetry — different
function (`vm_fault_unwire`), different mechanism (fictitious
misclassification + unchecked PHYS_TO_VM_PAGE), different fix locus.

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