DF-5655 / verdict.json
{ "finding_id": "DF-5655", "status": "reproduced", "verdict": "REPRODUCED (panic, 2/2 deterministic): the vkernels sys_set_tls_area performs no canonicality validation of the user-supplied 64-bit tls_info.base (sys/platform/vkernel64/x86_64/tls.c:96-102 \u2014 only info.size < -1 is checked) and stores it into td_tls, which aliases td_mach.mtd_savevext.vx_tls (vkernel64/include/thread.h:50). On the syscall-return path go_user() hands &curthread->td_savevext verbatim to the HOST vmspace_ctl(VMSPACE_CTL_RUN) (vkernel64 trap.c:1407-1408); the host copies it in (vm/vm_vmspace.c:293-295), pc64 cpu_sanitize_tls() is a no-op (pc64 machdep.c:924-927), and host set_user_TLS() executes wrmsr(MSR_KGSBASE, 0x0000800000000000) (pc64 tls.c:163-172) -> #GP(0) in HOST kernel mode (Intel SDM Vol.3 17.17) -> Fatal trap 9 ... Stopped at set_user_TLS+0x94: wrmsr, host halts in DDB. RIP 0xffffffff80bd4c74 is the wrmsr after mov $0xc0000102,%ecx (MSR_KGSBASE) per objdump of the guest kernel; the FSBASE wrmsr was skipped because the trigger poisoned only GS. Consequence: ANY unprivileged user INSIDE a vkernel (demonstrated uid=65534 in-vkernel) panics the HOST kernel with one syscall \u2014 the vkernels containment is defeated; this is the vkernel twin of DF-5639 (pc64 direct syscall, RIP +0x68/FSBASE) reached through a different file and a different entry chain. Ceiling is a reliable host-kernel DoS: #GP is a fixed fault with no write primitive and no resumable path, so no uid=0 route exists. fix.diff (canonicality check in this file) was built into the vkernel in-guest and re-run: identical trigger now returns EINVAL (rc=22), vkernel and host stay alive, and legitimate TLS (rtld bootstrap of every exec, both fs and gs variants) is unaffected.", "reproduced": true, "impact": "panic", "confidence": "certain", "exploit_chain": "in-vkernel unprivileged user (uid 65534) -> syscall 472 set_tls_area(1, {base=0x0000800000000000, size=-1}, 16) -> vkernel stores td_tls.info[1] with no base validation (vkernel64 tls.c:102; set_user_TLS is a NOP, tls.c:142-145) -> syscall return: go_user() passes &td_savevext (== td_tls) to host vmspace_ctl(VMSPACE_CTL_RUN) (trap.c:1407-1408) -> host copyin vx_tls (vm_vmspace.c:293) -> cpu_sanitize_tls NOP (machdep.c:924) -> host set_user_TLS wrmsr(MSR_KGSBASE, non-canonical) (pc64 tls.c:171) -> #GP(0) in host kernel mode -> Fatal trap 9 -> host kernel dead in DDB. Stop at panic: no memory-corruption primitive (#GP is a fault, not a controlled write); impact ceiling = unprivileged in-vkernel -> host-kernel DoS, crossing the virtualization boundary.", "evidence": "panic.txt (verbatim serial capture: DF5655 uid=65534 marker then Fatal trap 9 ... Stopped at set_user_TLS+0x94: wrmsr); run.log (baseline narrative + RIP attribution via objdump: +0x94 == wrmsr after mov $0xc0000102,%ecx = MSR_KGSBASE); run.2.log (fixed vkernel, gs variant: RETURNED rc=22 = EINVAL, MARKER_AFTER_TRIGGER prints, vkernel+host alive); run.3.log (fixed, fs variant: rc=22); env.txt (kernel uname, KVM/GenuineIntel, rig + DF-5474 build-workaround notes, sha256s); build.log (full VKERNEL64 nativekernel build incl. tls.c recompile with the fix).", "kernel_refs": [ "sys/platform/vkernel64/x86_64/tls.c:96-102", "sys/platform/vkernel64/x86_64/tls.c:142-145", "sys/platform/vkernel64/include/thread.h:50", "sys/platform/vkernel64/x86_64/trap.c:1407-1408", "sys/vm/vm_vmspace.c:293-311", "sys/platform/pc64/x86_64/machdep.c:924-927", "sys/platform/pc64/x86_64/tls.c:163-172", "sys/cpu/x86_64/include/vframe.h:44-47" ], "poc_changes": "No seed PoC existed (pass-2 discovery). Trigger written from scratch as a static in-vkernel binary using a raw syscall-472 stub (no libc TLS dependence, raw write(2) + manual hex so nothing reads %fs/%gs after the poisoning); as_nobody wrapper proves in-vkernel uid/gid 65534; rig (vkroot image + VKERNEL64 boot) adapted from the DF-5607/DF-5579 rigs, with the vkernel console teed onto the guest serial console so the unprivileged marker and the host panic land in one transcript. The stock VKERNEL64 does not link without the DF-5474 defsym workaround (applied to the guests /usr/src only, identically in baseline and fixed phases).", "attempts": 4, "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 (host-of-vkernel guest kernel, stock snapshot; vkernel = /usr/obj/usr/src/sys/VKERNEL64/kernel.debug built in-guest, stock and fixed phases)", "runtime_sec": 55, "guest_dirty": 0, "build_cmd": "cc -static -O -o /tmp/vk_tls_noncanon /tmp/vk_tls_noncanon.c && cc -static -O -o /tmp/as_nobody /tmp/as_nobody.c && sh /tmp/vk_image.sh (vkernel: cd /usr/src && make -j6 nativekernel KERNCONF=VKERNEL64, with the DF-5474 defsym build workaround)", "run_cmd": "sh /tmp/vk_run.sh baseline 1 # baseline: host Fatal trap 9 at set_user_TLS+0x94 (MSR_KGSBASE wrmsr); fixed: sh /tmp/vk_run.sh fixed 1|0 -> rc=22 EINVAL, all alive", "code_hash": "1b61aff7eb928de073d85f6fd411d50a07139aa813f858093fe90d628a2c8770", "notes": "Distinct from DF-5639 (pc64 direct syscall -> FSBASE wrmsr +0x68, host user): this finding lives in the vkernel64 twin file, is reachable by an UNPRIVILEGED USER INSIDE THE VKERNEL (no host privilege at all), and faults the host through the go_user/vmspace_ctl vframe handoff (MSR_KGSBASE wrmsr +0x94) \u2014 a virtualization-boundary escape of the same sink. Once hosts adopt DF-5639s cpu_sanitize_tls fix, an unfixed vkernel would instead die at trap.c:1427-1430 panic(vmspace_ctl failed) \u2014 this fix prevents that too. Negatives killed: which/index bounds OK (tls.c:79,127); infosize<0 checks are dead code (size_t, sysproto.h:1045) but all copies bounded by min(sizeof(info),infosize)<=16 with pre-zeroed partial path (tls.c:87-93,134); no get_tls_area leak (td_tls only ever user-set/fork-inherited/zeroed); no cothread %gs interplay (set_user_TLS NOP, swtch.s call site is #if 0); vmspace_ctl save/restore itself is sound (vm_vmspace.c:288-306,884). Guest was reset with-src after both phases (clean for next spawn).", "recommended_fix": "Reject non-canonical info.base in the vkernels sys_set_tls_area with EINVAL (see fix.diff); pair with DF-5639s pc64 cpu_sanitize_tls validation to seal the host boundary against any vkernel binary.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fixed: baseline (stock vkernel) \u2014 in-vkernel uid-65534 trigger panics the host at set_user_TLS+0x94 (MSR_KGSBASE wrmsr), guest dead in DDB; patched vkernel (fix.diff, canonicality check in vkernel64 tls.c) \u2014 identical trigger returns rc=22/EINVAL for both gs and fs variants, vkernel stays up (MARKER_AFTER_TRIGGER), host stays up (ssh fine), and legitimate TLS (rtld bootstrap of every binary execd in the vkernel) is unaffected.", "fix_evidence": "run.log + panic.txt (baseline panic) vs run.2.log / run.3.log (patched: DF5655: RETURNED rc=22, alive); build.log (patched VKERNEL64 build incl. tls.c recompile); env.txt (RIP->wrmsr attribution)." } |