{
  "finding_id": "DF-2816",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "boot() calls shutdown_cleanup_proc(curproc) (kern_shutdown.c:291) on the still-running reboot(2)-calling process while its sibling threads keep executing on other CPUs (only initproc is SIGSTOPped first, :293-297; other CPUs are never stopped in the sys_reboot path). The cleanup closes every fd, cache_drop()s - and thereby NULLs (vfs_cache.c:1090-1096) - fd_ncdir/fd_nrdir/fd_njdir, and vm_map_remove()s the whole user address space (:601-609). A sibling thread that then performs any path lookup copies the NULL-ed nchandle into nd->nl_nch (nlookup_init has no NULL-ncp guard for the cwd/root anchors, vfs_nlookup.c:149-161) and naccess() dereferences the NULL ncp at +0x58 (vfs_nlookup.c:655-659; disasm naccess+0x33 'movzwl 0x58(%r15),%edx' with r15==NULL) -> kernel page fault -> panic. Reproduced deterministically two ways on the stock INVARIANTS guest: (1) race2 - 4 sibling threads spamming ioctl(DIOCGKERNELDUMP) while the main thread calls reboot(RB_DUMP): panic 'Fatal user address access from kernel mode', fault VA 0x58, naccess+0x33 <- nlookup+0x15e, 2/2 runs; (2) sibs - 4 siblings looping only open(\"etc/passwd\") with main calling reboot(RB_DUMP|RB_NOSYNC): identical naccess+0x33 panic, then the machine hard-wedges (endless 'send_ipiq 2->4 tgt not draining', no DDB, no reboot), 1/1. Requires the reboot privilege (SYSCAP_NOREBOOT) - an already-privileged caller converting a clean reboot into a kernel panic/wedge and losing the crash dump; not an escalation (NULL deref only; page 0 unmappable on x86-64). Fix validated in the same rebuilt kernel: only shutdown_cleanup_proc(curproc) when p_nthreads < 2; 'sibs 4' then reboots cleanly with a completed dump and no panic (run.patched.log).",
  "exploit_chain": "privileged multi-threaded process -> N sibling threads loop relative-path open() (or any syscall doing path lookups / copyin of user memory) -> main thread calls reboot(2) -> boot() tears down the process's fds, NULLs fd_ncdir.ncp, removes its user address space -> sibling's next open() runs nlookup_init -> cache_copy of NULL-ncp fd_ncdir into nd->nl_nch -> naccess() NULL+0x58 deref -> kernel panic (and in the observed run, a hard IPI wedge). No uid0 route: pure NULL-deref DoS at shutdown time.",
  "evidence": [
    "serial.race2-panic.log - clean panic trace: naccess+0x33 <- nlookup+0x15e, fault VA 0x58, process race2, churn cpu (2/2 runs)",
    "serial.race2-full.log - full serial log of that run",
    "serial.sibs-panic.log - minimal deterministic sibs trigger: same naccess+0x33 panic, then 'send_ipiq 2->4 tgt not draining' hard wedge",
    "sibs.c / race2.c - PoC sources; run.sh - recipes",
    "run.patched.log - fix validation on patched kernel #1: sibs 4 -> full dump ('Dumping 676 MB ... Dump complete') and clean reboot, no panic, no wedge",
    "fix.diff - validated diff (curproc cleanup only when single-threaded)"
  ],
  "kernel_refs": [
    "sys/kern/kern_shutdown.c:290-301",
    "sys/kern/kern_shutdown.c:566-610",
    "sys/kern/vfs_cache.c:1090-1096",
    "sys/kern/vfs_nlookup.c:145-177",
    "sys/kern/vfs_nlookup.c:655-659"
  ],
  "poc_changes": "Seed had no PoC; developed two triggers. race2 (ioctl-churning siblings + reboot(RB_DUMP)) reproduced 2/2 but the exact sibling syscall reaching nlookup was not pinned; sibs.c (4 siblings looping open(\"etc/passwd\") + reboot(RB_DUMP|RB_NOSYNC)) was written from the code analysis (fd_ncdir anchor NULL) and reproduced the identical naccess+0x33 panic 1/1, deterministically, with a 30-line program and no dump-device churn at all.",
  "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",
  "runtime_sec": 900,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -Wall -pthread -o /root/sibs /root/sibs.c && cc -O2 -Wall -pthread -o /root/race2 /root/race2.c",
  "run_cmd": "sysctl debug.debugger_on_panic=0 debug.trace_on_panic=1; /root/sibs 4   # reboots; panic+wedge visible on serial console (dfbsd-qemu/vm.sh log)",
  "code_hash": "sibs.c=774993a5dd260a65a93a71dc2369daccd407c196938aeca09b6b7d7c17ec22fe race2.c=6829b6869d09a58b666245e75a3a4280350781d4107695dc6f3d0552b14336bc",
  "notes": "naccess+0x33 disassembly (stock kernel binary): 'mov (%rsi),%r15' (r15=nch->ncp) then 'movzwl 0x58(%r15),%edx' - NULL ncp confirmed as the faulting dereference; nlookup+0x15e resolves to vfs_nlookup.c:658 (directory-search permission check on nd->nl_nch). The panic-path boot() is unaffected (panicstr != NULL skips the cleanup). The panic sometimes garbles the console because two CPUs print simultaneously (itself evidence of the multi-CPU crossfire). Recommended additional hardening outside this file: NULL-guard cache_copy of fd_ncdir/fd_nrdir in nlookup_init (vfs_nlookup.c:149-161), mirroring the existing fd_njdir.ncp checks at :153/:163.",
  "recommended_fix": "Only run shutdown_cleanup_proc(curproc) when the calling process is single-threaded (curproc->p_nthreads < 2); plus NULL-guard the cwd/root nchandle anchors in nlookup_init() for defense in depth.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Tue Sep  1 17:21:28 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Same rebuilt kernel as DF-2815 (fix.diff includes the DF-2816 hunk). Patched-kernel run of the exact sibs 4 trigger: no naccess panic, no Fatal trap, no IPI wedge; the shutdown proceeds to a full dump ('Dumping 676 MB ... Dump complete') and a clean automatic reboot (run.patched.log). Baseline on stock kernel #0: panic naccess+0x33 plus hard machine wedge.",
  "fix_evidence": [
    "run.patched.log - patched run: full dump + clean reboot under the identical sibling workload",
    "fix.diff - the validated diff"
  ]
}
