DF-2994 / verdict.json
{ "finding_id": "DF-2994", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "With an export configured '-public -index=<missing file>', a v3 LOOKUP using the public filehandle (fhlen=0) for any directory lacking the index file makes nfsrv_lookup vput() a vnode it unlocked at line 565 and never re-locked \u2014 vput -> vn_unlock -> lockmgr(LK_RELEASE) on an unheld lock panics unconditionally. Reproduced on the stock INVARIANTS guest with the exact predicted backtrace (panic: lockmgr: LK_RELEASE: no lock held / vput / nfsrv_lookup); guest dropped to DDB, ssh dead.", "exploit_chain": "single pre-auth-style UDP LOOKUP(public fh, '/') -> deterministic kernel panic (remote DoS) whenever WebNFS is exported with an index file", "evidence": [ "panic.txt \u2014 full serial-console panic text with the vput()/nfsrv_lookup() backtrace and db> prompt", "run.log \u2014 NULL baseline healthy, trigger line, ssh never returns, vm.sh status down", "VERDICT.md \u2014 line-accurate chain: nfs_serv.c:565 vn_unlock, 568-590 index-lookup failure skips the re-lock/cache_vget block, 599/643 vput on unlocked vp; vfs_lock.c:703-707; kern_lock.c:767-768" ], "kernel_refs": [ "sys/vfs/nfs/nfs_serv.c:565", "sys/vfs/nfs/nfs_serv.c:574", "sys/vfs/nfs/nfs_serv.c:599", "sys/vfs/nfs/nfs_serv.c:643", "sys/kern/vfs_lock.c:703", "sys/kern/kern_lock.c:767", "sys/vfs/nfs/nfs_subs.c:1183", "sys/kern/vfs_subr.c:2266" ], "poc_changes": "wrote nfspoc.c from scratch (lookup0 subcommand: v3 LOOKUP with fhlen=0 public filehandle)", "attempts": 1, "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", "runtime_sec": 20, "guest_dirty": 0, "build_cmd": "cc -O -o /root/nfspoc /root/nfspoc.c", "run_cmd": "NFSPOC_SRCPORT=831 NFSPOC_TIMEOUT=5 /root/nfspoc lookup0 /", "code_hash": "7faf5661fb7604b4672cb2d79210c692f0f3611e6a0159d6083684f27cee7f2c", "notes": "Preconditions: NFS export with -public and index=<file> (WebNFS index); index file absent from the looked-up directory. vfs.nfs.nfs_privport=1 required a reserved source port. Panic is unconditional in this lockmgr (not INVARIANTS-only).", "recommended_fix": "Re-lock the original vp (vn_lock(vp, LK_EXCLUSIVE|LK_RETRY)) when the public-index nlookup fails, before continuing (fix.diff).", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Fri Sep 4 21:02:10 UTC 2026\troot@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Patched kernel answers the identical public-fh LOOKUP with a normal reply; no panic, guest stays up. Bad behavior gone.", "fix_evidence": [ "run.fixed.log", "panic.txt" ] } |