# DF-5579 — vkernel64 `kvm_access_check()` /dev/kmem validator kills the vkernel on ungrown KVA (`vtopde()` assert → SIGABRT; DF-5358 pc64 twin)

**File:** sys/platform/vkernel64/x86_64/vm_machdep.c (pass 2)
**Severity:** Low (root-in-vkernel required) · **Confidence:** certain
**Status:** reproduced (panic of the whole vkernel) · fix validated

## What breaks

`kvm_access_check()` (sys/platform/vkernel64/x86_64/vm_machdep.c:310-329) is
the residency gate for `/dev/kmem` (kern_memio.c:285). For every page of the
requested range it calls `pmap_kextract(addr)` (vm_machdep.c:322-325) **before**
any of the range has been proven mapped. The vkernel's `pmap_kextract()`
(sys/platform/vkernel64/platform/pmap.c:717-747) does `pde = *vtopde(va)`
(pmap.c:732), and `vtopde()` (pmap.c:353-358) is:

```c
x = pmap_pde(kernel_pmap, va);
assert(x != NULL);          /* pmap.c:357 — fires on ungrown KVA */
```

`pmap_pde()` (pmap.c:273-280) returns NULL when the 1 GB PDPT entry covering
the VA is invalid — i.e. for any KVA beyond the `pmap_growkernel()` frontier
(`kernel_vm_end`, absolute VA, initialized to `KvaStart` at pmap.c:1563-64).
The vkernel's kernel VA space is a fixed 512 GB `[0x8000000000, 0x10000000000)`
(vmparam.h:80-81, MAP_FIXED at init.c:609-621) of which only a few hundred MB
is ever grown. A single `pread(2)` on `/dev/kmem` at e.g. 0xE400000000
(512 GB + 400 GB) therefore SIGABRTs the entire vkernel from inside the
validator. `assert()` is live (libc assert, no -DNDEBUG in the vkernel build)
and SIGABRT is not in the vkernel's handled signal set (exception.c:226-250).

This is the vkernel twin of DF-5358 (pc64 `pmap_kextract` recursive-PDE fault);
same class (validator-fault on ungrown KVA), different mechanism (assert/abort
vs page-fault-in-recursive-map).

## Reproduce (guest, as root)

```
# one-time: DF-5474 link workaround (see findings/poc/DF-5474)
#   sed -i.bak -e "s|vers.o -pthread$|& -Wl,--defsym,__build_id_start=0 -Wl,--defsym,__build_id_end=0|" \
#       /usr/src/sys/platform/vkernel64/conf/Makefile
#   sysctl vm.vkernel_enable=1 ; mkdir -p /var/vkernel
cc -static -O2 -o /root/vkmem vkmem.c      # build.log
sh build_image.sh                          # /vkroot.img with /root/vkmem
sh run.sh                                  # boots kernel.debug, runs control+trigger
```

Expected (unfixed):

* `/root/vkmem control`  (pread at 512GB+1MB): `pread returned -1 errno=14`,
  `DF5579_control_SURVIVED` — validator safe on grown KVA.
* `/root/vkmem 0x454780`  (vkernel image text, shortcut path): `pread returned 8`,
  bytes `41 56 41 55 41 54 53 48` — normal /dev/kmem reads work.
* `/root/vkmem trigger`  (pread at 512GB+400GB):
  `Assertion failed: (x != NULL), function vtopde, file .../pmap.c, line 357.`
  → **vkernel process dies** (`=== vkernel alive? === DEAD`).

Fixed kernel (fix.diff applied, rebuilt): trigger returns `pread returned -1
errno=14` + `DF5579_trigger_SURVIVED`, run reaches `VKDONE`, vkernel ALIVE;
both controls byte-identical to baseline (run.fixed.log).

## Fix

`fix.diff` — gate the page loop on `kernel_vm_end` (declared in vm/vm_kern.h,
already included) after the KvaStart/KvaEnd bounds, before the first
`pmap_kextract()`.
