DF-3236 / verdict.json
{ "finding_id": "DF-3236", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED on stock INVARIANTS kernel: a root user calling fhopen(2) with a devfs file handle whose fid_gen matches boottime.tv_sec but whose fid_ino matches no devfs node drives devfs_vfs_fhtovp (devfs_vfsops.c:214) -> devfs_inode_to_vnode (devfs_core.c:959) into vn_lock(NULL) at devfs_core.c:972 because the core workers NULL result is locked before the callers NULL check (devfs_vfsops.c:216) can run -> lockmgr_exclusive+0x5d reads NULL+const -> Fatal trap 12, fault VA 0x0, guest down. Unprivileged users cannot reach it: every local fh* syscall is SYSCAP_RESTRICTEDROOT-gated (verified EPERM live); VFS_VGET exposure requires an NFS-exported devfs (admin action). One-line NULL guard fix validated on a rebuilt guest kernel: same PoC returns ENOENT and the guest stays up. Root-gated deterministic NULL-deref DoS, no corruption/escalation primitive -> Low.", "exploit_chain": "None: deterministic NULL-pointer read at a fixed lockmgr offset; panic-only. Privilege boundary held (unprivileged run = EPERM at the syscall gate).", "evidence": "panic.txt:40-59 (Fatal trap 12, fault VA 0x0, Stopped at lockmgr_exclusive+0x5d, current process = fhdevfs); fix_run.log (patched kernel: survived, errno=2 ENOENT, uptime alive); VERDICT.md (full line-cited chain); fhdevfs.c (getfh-based byte-exact handle, ino swap)", "kernel_refs": [ "sys/vfs/devfs/devfs_core.c:971-972", "sys/vfs/devfs/devfs_core.c:1373-1378", "sys/vfs/devfs/devfs_core.c:630-649", "sys/vfs/devfs/devfs_core.c:2196-2214", "sys/vfs/devfs/devfs_vfsops.c:203-221", "sys/vfs/devfs/devfs_vfsops.c:244-256", "sys/kern/vfs_syscalls.c:4814-4856" ], "poc_changes": "Seed-free (fresh PoC). Two iterations: (1) first version derived fid_gen from sysctl kern.boottime -> failed devfs_vfs_fhtovps gen check with EINVAL (sysctl tv_sec does not byte-match the kernel boottime global); (2) final version takes a real devfs handle via getfh(\"/dev/null\") and swaps only fid_ino to 0x41414141 - baseline panicked immediately.", "attempts": 2, "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 (baseline); #1: Wed Sep 9 03:33:19 UTC 2026 (patched)", "runtime_sec": 2460, "guest_dirty": 0, "build_cmd": "cc -O -o /root/df3236/fhdevfs /root/df3236/fhdevfs.c", "run_cmd": "/root/df3236/fhdevfs (root; unprivileged control: /tmp/fhdevfs)", "code_hash": "b79729f303b3fef199bcc6581d418d3413019173c7550a0d2e730d0822cc5529", "notes": "devfs_vfs_vget shares the broken helper; its only in-tree caller path is the kernel NFS server wire walk (nfs_serv.c:3417/3454), so an exported devfs would make this unauthenticated-remote (admin opt-in, not default). Guest reset (with-src) after validation.", "recommended_fix": "Guard vn_lock on the worker result in devfs_inode_to_vnode(): `if (vp != NULL) vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);` (devfs_core.c:972) - validated on rebuilt kernel.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Wed Sep 9 03:33:19 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Applied fix.diff inside guest /usr/src, make nativekernel + installkernel (fix_build.log RC=0), rebooted into kernel #1; exact same PoC under root now returns ENOENT (devfs_vfs_fhtovp:216-217 NULL branch reachable) with the guest staying up - panic gone.", "fix_evidence": "fix_run.log (survived errno=2 + uptime); fix_build.log (kernel build completed 03:36:53, install completed 03:45:37); env.txt (patched uname)" } |