⬢ DragonFlyBSD Kernel Audit
DF-3236 / verdict.json
← back to finding ↓ download raw
{
  "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)"
}