{
  "finding_id": "DF-5831",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "nlookup()s internal doretry path (sys/kern/vfs_nlookup.c:1380) executes cache_copy(nd->nl_basench, &nd->nl_nch) with nl_basench == NULL whenever the nd was initialized by nlookup_init_raw()/nlookup_init_root() (vfs_nlookup.c:265 vs :280-290/:325-334), because those initializers never set nl_basench. Every NFS-server name RPC resolves through nfs_namei() (nfs_subs.c:1155) which uses nlookup_init_raw(), so any retry trigger during a server-side name lookup - a concurrent rename bumping the ncp nc_generation (detected at vfs_nlookup.c:1313-1332), ESTALE, or a destroyed parent (:1229-1232) - NULL-derefs at cache_copy (ncp is offset 0 of struct nchandle; fault read at 0x0). Reproduced on the stock INVARIANTS guest on the FIRST run, in under 2 minutes: Fatal trap 12: page fault while in kernel mode, fault virtual address = 0x0, Stopped at cache_copy+0x11: movq (%rdi),%r13, current process = nfsd, immediately after the console showed a rename race on the looked-up directory. Impact is a remote kernel panic of the NFS server by any client permitted by the exports (lookups racing renames of the containing directory); deterministic NULL deref, no memory-corruption primitive, so panic/DoS class only. Fix (nl_basench_store field holding a private refd retry base, set by init_raw/init_root, dropped by nlookup_done) validated in-guest: kernel rebuilt with make nativekernel, rebooted, identical rig run twice for the full 300s window each with racers verified running - NO PANIC both times, guest stayed up.",
  "exploit_chain": "1) Attacker is any NFS client permitted by the servers exports (no auth beyond the export ACL). 2) Thread A issues LOOKUP-bearing RPCs (stat/open) on names inside an exported directory D (each becomes a server-side nfsrv_lookup -> nfs_namei -> nlookup_init_raw -> nlookup with nl_basench==NULL). 3) Thread B (second connection/client or local process) renames D <-> D2 in a loop; each rename bumps Ds ncp nc_generation via _cache_unlink_parent/_cache_link_parent (_cache_ncp_gen_enter/exit, vfs_cache.c:783/840/2155). 4) Any A-lookup in flight across a B-rename detects the generation change at vfs_nlookup.c:1313, sets doretry, and the retry block runs cache_copy(NULL, &nd->nl_nch) at :1380. 5) Fatal trap 12 read at 0x0 in cache_copy+0x11 inside the nfsd process - kernel panic, NFS server (and the whole machine) down. Observed in <=2 minutes of hammering; ordinary concurrent NFS workloads reach the same race without hostile intent.",
  "evidence": "panic.txt (full console panic: Fatal user address access from kernel mode from nfsd, fault virtual address = 0x0, Stopped at cache_copy+0x11: movq (%rdi),%r13); serial_console_baseline.log (console context incl. the preceding kern_rename: retry due to race on: sub -> sub2); run.log + run.2.log (post-fix survival runs, NO PANIC, 300s each); build.log/fix_build.log (kernel build with fix.diff, RC=0); fix.diff (validated fix)",
  "kernel_refs": [
    "sys/kern/vfs_nlookup.c:1380",
    "sys/kern/vfs_nlookup.c:1367-1385",
    "sys/kern/vfs_nlookup.c:255-291",
    "sys/kern/vfs_nlookup.c:297-335",
    "sys/kern/vfs_cache.c:996-1000",
    "sys/vfs/nfs/nfs_subs.c:1155",
    "sys/vfs/nfs/nfs_serv.c:521",
    "sys/kern/vfs_cache.c:783",
    "sys/kern/vfs_cache.c:840",
    "sys/kern/vfs_cache.c:2155",
    "sys/kern/vfs_nlookup.c:1313-1332",
    "sys/kern/vfs_nlookup.c:1229-1232"
  ],
  "poc_changes": "PoC authored fresh for this finding (no seed): hammer.c (NFS client LOOKUP hammer via lstat of random names under the mounted exports sub dir), renamer.c (sub<->sub2 ping-pong, run both locally on the export and over the NFS mount), run.sh (loopback NFS server+client setup adapted from the DF-3061 rig, 8 nfsd threads, 300s race window). All syscall usage compiled and ran first-try on the guest; MD5s verified after copy.",
  "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 (baseline) / #1: Tue Oct 13 10:58:07 UTC 2026 (fix-validation build)",
  "runtime_sec": 3480,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -pthread -o /tmp/hammer /root/poc/DF-5831/hammer.c && cc -O2 -o /tmp/renamer /root/poc/DF-5831/renamer.c",
  "run_cmd": "sh /root/poc/DF-5831/run.sh   (baseline: panic in nfsd <=2min; fixed kernel: NO PANIC after 300s, twice)",
  "code_hash": "43dd0f94a09434202198c4cafac6939c54879f2e1bb4dbd75cc7a1f54d8f9fde",
  "notes": "Guest reset (with-src) performed after verification, so the next spawn starts clean. Same-precedent rating as DF-3061 (nfs_namei retry family): remote panic of a non-default-service (nfsd) => High. The panic signature is distinct from DF-3061s lockmgr_release+0x11 double-release: this trap is a clean NULL read at 0x0 in cache_copy from nlookups own retry block, and DF-3061s fix does not cover it.",
  "recommended_fix": "Give nlookup_init_raw()/nlookup_init_root() a private, ref-held retry base: add struct nchandle nl_basench_store to struct nlookupdata, copy ncstart into it and point nl_basench at it in both initializers, and drop it in nlookup_done() (see fix.diff; validated in-guest).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff applied to the guests /usr/src (patch -p1, 4 hunks clean), kernel rebuilt with make nativekernel KERNCONF=X86_64_GENERIC (build.log, RC=0), installed and rebooted. The identical PoC rig then ran the full 300-second race window twice (run.log, run.2.log) with hammer/renamer processes verified running and the NFS mount verified active - no panic either time, guest stayed up, dmesg clean. Baseline had panicked in under 2 minutes. The fix is confirmed to remove the crash without changing any other behavior observed.",
  "fix_evidence": "fix.diff; fix_build.log (=build.log, full untrimmed 14897-line kernel build ending Kernel build for X86_64_GENERIC completed ... RC=0); fix_run.log & run.2.log (both end NO PANIC); panic.txt (baseline)"
}