{
 "finding_id": "DF-3241",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "Deterministic kernel NULL-deref panic reproduced on the stock INVARIANTS kernel: an fhstat(2) resolving a UFS mounts fsid inside the dounmount() window between VFS_UNMOUNT() (ffs_unmount frees the ufsmount, mp->mnt_data=0 at ffs_vfsops.c:862-866) and mountlist_remove() (vfs_syscalls.c:1040) faults at ffs_fhtovp+0xb reading 0x18(%rax) with rax=mnt_data=NULL. Baseline hit within 3 mount/umount -f rounds. vfs_getvfs() (vfs_mount.c:413-424) hands out mounts in that window because it never checks MNTK_UNMOUNT. Unauthenticated reachability exists via nfsrv_fhtovp() (nfs_subs.c:1235-1246) against any exported UFS with nfsd running; the fh* syscalls used for the PoC are root-gated (same accepted methodology as the DF-3236 devfs twin). Impact ceiling is a kernel DoS panic (fixed-address read of a NULL pointer field), not a corruption primitive; no uid0 chain exists for this bug.",
 "exploit_chain": "",
 "evidence": [
  "panic.txt: Fatal trap 12 ... fault virtual address = 0x18 + Stopped at ffs_fhtovp+0xb: movq 0x18(%rax),%r9 from process fhbomb (stock kernel, <=3 rounds)",
  "run.log: baseline host transcript and window analysis (0x18 == offsetof(struct ufsmount, um_fs))",
  "run.fix.log: fix-v2 kernel SANITY_OK + round 6000 done + SURVIVED 6000 rounds without panic",
  "VERDICT.md: why fix v1 (vfs_getvfs flag filter alone) still panicked and why the vfs_busy() interlock closes it",
  "fix.diff: 5-hunk validated fix (git-apply clean)"
 ],
 "kernel_refs": [
  "sys/vfs/ufs/ufs_vfsops.c:242-246",
  "sys/vfs/ufs/ffs_vfsops.c:1215-1219",
  "sys/vfs/ufs/ffs_vfsops.c:862-866",
  "sys/kern/vfs_syscalls.c:1006",
  "sys/kern/vfs_syscalls.c:1040",
  "sys/kern/vfs_mount.c:413-424",
  "sys/vfs/nfs/nfs_subs.c:1235-1246"
 ],
 "poc_changes": "No seed PoC existed; written from scratch for this finding. Fixes during bring-up: ESTALE needs sys/errno.h on DragonFly; guest sh choked on a stray * comment line in race.sh; DFly vnconfig takes no -s size arg (size comes from the backing file, created with dd); truncate -s 64m unsupported, used dd; newfs on /dev/vn0 auto-creates the compatibility slice (mounted as /dev/vn0s0).",
 "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": 3780,
 "guest_dirty": 0,
 "build_cmd": "host: bash findings/poc/DF-3241/build.sh; guest: cc -O2 -o fhbomb fhbomb.c (etc.); fix kernel: cd /usr/src && make -j6 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC",
 "run_cmd": "guest(root): ROUNDS=6000 NBOMB=8 sh /root/poc/race.sh",
 "code_hash": "459441aa6038f35135d2f536019a19317b3eb27eafdde72fe24b1dfe0429f02c",
 "notes": "code_hash is sha256 of fhbomb.c (trigger); race.sh sha256 = 942bbbb16ea6bcaa632c9bd9bd4a3517c772e2d43863d1ef8f49a53d29bc6155. Fix iteration result worth upstream attention: a vfs_getvfs() MNTK_UNMOUNT filter ALONE is insufficient (proven by a second panic on the fix-v1 kernel) because check-and-use is preemptible; the vfs_busy(mp, LK_NOWAIT) interlock (shared mnt_lock excludes the whole dounmount body incl. forced unmount) is required. fhopen/fhstatfs/fhstatvfs and kern_checkpoint should get the same vfs_busy pattern (mechanical follow-up, root-gated paths). Post-panic disk state preserved in qcow2 snapshot df3241-postpanic. Guest reset to with-src after validation.",
 "recommended_fix": "Interlock fsid-based file-handle resolution against unmount: vfs_busy(mp, LK_NOWAIT) around VFS_CHECKEXP/VFS_FHTOVP in nfsrv_fhtovp() and sys_fhstat() (and the remaining fh* syscalls), bail with ESTALE on failure; additionally filter MNTK_UNMOUNT mounts in vfs_getvfs() and NULL-guard mnt_data in ufs_check_export()/ffs_fhtovp().",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 9 05:56:10 UTC 2026   root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Validated on-guest: baseline (stock #0 kernel) panicked at ffs_fhtovp+0xb within 3 rounds; a first fix attempt (vfs_getvfs MNTK_UNMOUNT filter only, kernel built and booted) STILL panicked within ~20 s, proving the preemptible check-and-use TOCTOU; the final 5-hunk fix (vfs_busy interlock in sys_fhstat + nfsrv_fhtovp + vfs_getvfs filter + NULL guards in ufs_check_export/ffs_fhtovp, fix.diff) survived 6000/6000 mount+umount -f rounds with 8 fhstat spinners and zero panics (run.fix.log).",
 "fix_evidence": [
  "fix.diff (git apply --check clean, 6 hunks across 5 files)",
  "run.fix.log: SANITY_OK, round 6000 done, SURVIVED 6000 rounds without panic",
  "build.log: full nativekernel build of the patched kernel (3756 objects relinked)",
  "serial log of fix-v1 panic captured in session (same ffs_fhtovp+0xb signature) documented in VERDICT.md"
 ]
}