DF-5579 / verdict.json
{ "finding_id": "DF-5579", "status": "reproduced", "verdict": "Reproduced on the guest vkernel rig: a single 8-byte pread(2) on /dev/kmem at 0xE400000000 (inside the vkernels fixed 512GB KVA window [0x8000000000,0x10000000000) but beyond the pmap_growkernel() frontier) aborts the entire vkernel from inside the kvm_access_check() residency validator: pmap_kextract() (pmap.c:732) derefs vtopde() whose assert(x != NULL) (pmap.c:357) fires because pmap_pde() (pmap.c:273-280) returns NULL for the ungrown 1GB PDPT entry; libc assert -> SIGABRT (unhandled, exception.c:226-250) -> vkernel process DEAD with all its guests. Controls prove /dev/kmem works normally in the same boot (image-shortcut text read returns real kernel prologue bytes; grown-KVA read returns clean EFAULT). Root-in-vkernel required (kern_memio.c:841, 0640 root:kmem) -> Low severity, availability-only, pc64 DF-5358 twin. fix.diff (gate the page loop on kernel_vm_end, vm/vm_kern.h:107) validated by rebuild: trigger now returns EFAULT and the vkernel stays ALIVE to VKDONE with byte-identical positive control.", "reproduced": true, "impact": "panic", "confidence": "certain", "exploit_chain": "root inside vkernel -> open /dev/kmem -> pread at KvaStart+400GB (any address beyond kernel_vm_end within the 512GB MAP_FIXED KVA window) -> memrw -> kvm_access_check (vm_machdep.c:322-325) -> pmap_kextract -> vtopde assert -> SIGABRT -> whole vkernel process dies (all vcpus, all guest processes). No memory-safety primitive; no guest-user reachability; availability only.", "evidence": "panic.txt + run.baseline.log (assertion line + === vkernel alive? === DEAD); run.ctl.log (positive control: pread at 0x454780 returns 8 bytes 41 56 41 55 41 54 53 48 via the _start.._end shortcut; control EFAULT SURVIVED); run.fixed.log (post-fix: trigger EFAULT SURVIVED, VKDONE, ALIVE); vkfixbuild.log (fixed kernel build); VERDICT.md for the full chain", "kernel_refs": [ "sys/platform/vkernel64/x86_64/vm_machdep.c:310", "sys/platform/vkernel64/x86_64/vm_machdep.c:322", "sys/platform/vkernel64/platform/pmap.c:732", "sys/platform/vkernel64/platform/pmap.c:357", "sys/platform/vkernel64/platform/pmap.c:273", "sys/platform/vkernel64/platform/pmap.c:1563", "sys/platform/vkernel64/include/vmparam.h:80", "sys/platform/vkernel64/platform/init.c:609", "sys/kern/kern_memio.c:285", "sys/platform/vkernel64/x86_64/exception.c:226" ], "poc_changes": "vkmem.c written fresh (no seed); rig rebuilt from scratch on a reset guest: DF-5474 SYSTEM_LD defsym workaround re-applied, VKERNEL64 kernel rebuilt, /vkroot.img regenerated (DF-5337/5538/5556 rig), vm.vkernel_enable=1 + /var/vkernel created (both required and missing on a fresh guest).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (vkernel under test: DragonFly 6.5-DEVELOPMENT #0: Wed Oct 7 23:10:40 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/VKERNEL64)", "runtime_sec": 1080, "guest_dirty": 1, "build_cmd": "cc -static -O2 -o /root/vkmem vkmem.c && sh build_image.sh && cd /usr/src && make -j6 nativekernel KERNCONF=VKERNEL64", "run_cmd": "sh run.sh (boots /usr/obj/usr/src/sys/VKERNEL64/kernel.debug -m 512m -r /vkroot.img -n 1 -e init_path=/bin/vkinit; runs /root/vkmem control then /root/vkmem trigger inside)", "code_hash": "c253c1b2ea109ad85805bed1a2394c9ca4d4e8fd488d612ae8a12c7b20335173", "notes": "3 boots total (baseline trigger-death, baseline positive-control, fixed trio). Zero-PDE-within-grown-range case reads DMAP(0) garbage inside pmap_kextract but is caught by kernacc (vm_machdep.c:326) -> benign; only the PDPT-invalid case crashes. The vkernel kernel build itself required the DF-5474 link workaround (already-filed finding). Pass-2 negative results for the rest of the file are in the audit JSON: cpu_fork FP pcb copy is behaviorally inert for the child (TDF_USINGFP not inherited -> npxdna()->npxinit() clobbers pcb_save at first FP use -> child sees INIT state, therefore NO fork-path amplification of the DF-5556 exec-boundary XMM leak); LDT branches dead (sysarch EOPNOTSUPP, pcb_ldt always NULL, user_ldt_* are panic stubs); PCB_DBREGS/reset_dbregs unreachable (fill/set_dbregs return ENOSYS); pcb_onfault never set in vkernel; cpu_prepare_lwps copyout of the NULL return frame is bounded by the guest vm_map fault (kernel-range stacks fail with EFAULT via copyio.c:363-367).", "recommended_fix": "In kvm_access_check(), reject eaddr > kernel_vm_end (grown page-table frontier, vm/vm_kern.h) before the pmap_kextract() page loop (fix.diff).", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "Applied fix.diff to the guest /usr/src copy, rebuilt VKERNEL64 (vkfixbuild.log, BUILD_RC=0), re-ran the exact trio (run_fixed.sh -> run.fixed.log): trigger now returns pread=-1/EFAULT with DF5579_trigger_SURVIVED, session reaches VKDONE, vkernel ALIVE; positive text control returns the identical 8 bytes and KVA control behaves identically to baseline. The previously-observed bad behavior (assertion + vkernel death) is gone.", "fix_evidence": "run.fixed.log (DF5579_trigger_SURVIVED / ALIVE / VKDONE) vs run.baseline.log (Assertion failed pmap.c:357 / DEAD); vkfixbuild.log (fixed kernel rebuild); fix.diff (git-apply-able)" } |