# DF-5289 — vkernel pmap wire-accounting corruption via mlock() of UKSMAP mappings

## What (the bug)

`pmap_enter()` (sys/platform/vkernel64/platform/pmap.c:2429-2442) marks a PTE
`VPTE_WIRED` and bumps `pm_stats.wired_count` whenever `wired=TRUE`, but the
`vm_page_wire(m)` it performs is a no-op for fictitious pages
(`vm_page_wire()` skips `PG_FICTITIOUS` — sys/vm/vm_page.c). For
`VM_MAPTYPE_UKSMAP` mappings (/dev/upmap, /dev/kpmap, /dev/lpmap),
`vm_fault()` (sys/vm/vm_fault.c:562-580) fabricates a **stack-allocated**
fictitious `vm_page` (`fakem`) and passes it to `pmap_enter()` — so a wired
UKSMAP PTE is installed with **no wire count taken anywhere**.

On unwire, `pmap_unwire()` (pmap.c:2652-2657) does

```c
pa = *pte & VPTE_FRAME;
m = PHYS_TO_VM_PAGE(pa);	/* held by wired count */
```

for ANY wired PTE — including the unmanaged UKSMAP PTE — and hands the
**real vm_page of the kmalloc'd kernel page** (the upmap is
`kmalloc(M_WAITOK)` — kern_proc.c:1272) back to `vm_fault_unwire()`
(sys/vm/vm_fault.c:2690-2697), which then does
`vm_page_busy_wait(m); vm_page_unwire(m, 1);` on a page that was **never
wired through the pmap** (kernel slab pages carry exactly one wire from
`kmem_slab_alloc()`'s `pmap_enter(kernel_pmap, ..., wired=1)` —
sys/kern/kern_slaballoc.c:1830).

The trigger gate that lets this happen: `vm_fault_unwire()`'s `fictitious`
classification (vm_fault.c:2679-2681) checks only
`OBJT_DEVICE/OBJT_MGTDEVICE` — `VM_MAPTYPE_UKSMAP` (whose `ba.object` is
NULL) is misclassified as non-fictitious, unlike `vm_fault_wire()`
(vm_fault.c:2607-2620) which classifies UKSMAP correctly.

## Impact

* **1st mlock/munlock cycle** (silent corruption): the live kernel slab
  page backing the upmap drops `wire_count` 1 → 0 while still kmalloc-owned
  and kernel-mapped — a page-reclaim/UAF-class memory-corruption primitive.
* **2nd cycle** (deterministic): `vm_page_unwire()` with `wire_count`
  already 0 → `panic("vm_page_unwire: invalid wire count")`.

Privilege gate: `sys_mlock` requires `SYSCAP_RESTRICTEDROOT`
(sys/vm/vm_mmap.c:1024-1033; `pmap_wired_count` is not defined anywhere, so
the `#else` priv check always applies) — root *inside the vkernel* is
needed. The same shared-vm_fault.c chain exists on pc64 (same gating), so
this is not vkernel-only code rot; the pmap.c half under audit is the
vkernel64 one.

Verified on the guest with a vkernel-in-QEMU-guest run: baseline vkernel
panics at exactly the predicted point; kernel built with `fix.diff` runs
the identical PoC cleanly.

## Files

- df5289.c                 PoC source (mmap /dev/upmap + 4x mlock/munlock)
- run.sh                   guest-side driver (boots vkernel, feeds console)
- run.baseline.log         vulnerable vkernel: full console incl. panic + backtrace
- run.fixed.log            fixed vkernel: 4 clean cycles, shell alive
- panic.txt                the decisive panic excerpt
- build.log                vkernel build tail (nativekernel VKERNEL64)
- fix-build.log            patched-kernel rebuild log
- fix.diff                 two-layer fix (vm_fault.c classification mirror +
                          pmap_unwire() managed-only vm_page)
- README.md / VERDICT.md / manifest.json / verdict.json

## Reproduce

In the guest (root), with /usr/src present:

```
# one-time env:
sysctl vm.vkernel_enable=1
mkdir -p /var/vkernel
# vkernel64 link fix (unrelated build rot at this commit: kern_mib.c
# references __build_id_start/end which the userspace-style SYSTEM_LD
# never defines):
printf '\nchar __build_id_start[1] = { 0 };\nchar __build_id_end[1] = { 0 };\n' \
    >> /usr/src/sys/platform/vkernel64/platform/init.c
cd /usr/src && make -j6 nativekernel KERNCONF=VKERNEL64

# root image: whole-disk UFS (vkd0 has no partitions; answer the
# mountroot prompt with "ufs:vkd0" — the driver pre-feeds it):
vnconfig -c /dev/vn0 /vkroot.img
newfs /dev/vn0; mount /dev/vn0 /mnt
# populate: /bin/{sh,cat,ls,...}, /lib, /libexec/ld-elf.so.2, /root/poc
cc -static -O -o /mnt/root/poc df5289.c
cc -static -O -o /mnt/bin/vkinit vkinit.c   # console opener -> exec sh
umount /mnt; vnconfig -u vn0

sh run.sh            # baseline: panic at cycle 1 munlock
patch -p1 < fix.diff # in /usr/src
(cd /usr/obj/usr/src/sys/VKERNEL64 && make KERNEL=kernel all)
sh run.sh            # fixed: "SURVIVED - no panic"
```

Expected baseline:

```
DF5289: cycle 0 munlock = 0 errno=0 (Undefined error: 0)
DF5289: cycle 1 mlock   = 0 errno=0 (Undefined error: 0)
DF5289: cycle 1 munlock ...
panic: vm_page_unwire: invalid wire count: 0
vm_page_unwire() at ...
vm_fault_unwire() at ...
vm_map_user_wiring() at ...
sys_munlock() at ...
```

Expected fixed: all four cycles return 0, `DF5289: SURVIVED - no panic`.
