# DF-3230 — post-unmount fstatfs(2) NULL-deref panic in fuse_statfs → fuse_ipc_get

**Status: REPRODUCED (deterministic, minimal PoC + 2 prior in-staging
occurrences, identical signature) — panic — fix VALIDATED in-guest.**

## Root cause

* `fuse_unmount()` stores `mp->mnt_data = NULL` at fuse_vfsops.c:294 as
  its last substantive action before returning to userspace.
* An `fstatfs(2)` on a still-open fd whose file was on the fuse mount
  reaches `kern_fstatfs` (kern/vfs_syscalls.c:1495-1496), which takes
  the mount from `fp->f_nchandle.mount` (or the vnode's `v_mount`) —
  the `struct mount` is still valid and referenced; no vnode ops run,
  so no dead-vnode guard can intercept.
* `fuse_statfs()` (fuse_vfsops.c:386-414) does
  `fmp = VFSTOFUSE(mp)` — now NULL — with **no NULL / dead guard**, and
  calls `fuse_ipc_get(fmp, 0)`.
* `fuse_ipc_get()` (fuse_ipc.c:100) executes
  `atomic_fetchadd_long(&fmp->unique, 1)` → fetch-add at NULL+0x160 →

```
Fatal user address access from kernel mode from df3230 at ffffffff826094b3
Fatal trap 12: page fault while in kernel mode
fault virtual address	= 0x160
fault code		= supervisor write data, page not present
instruction pointer	= 0x8:0xffffffff826094b3
current process		= 1058 (df3230)
Stopped at      fuse_ipc_get+0x63:      lock xaddq      %rax,0x160(%r13)
```

Distinct from DF-3193 (freed-`fmp` walk on the *wake* path of an
in-flight statfs): this is the *entry* path with a NULL `fmp` after a
completed unmount, and it is deterministic — any fstatfs on an fd that
outlives its fuse mount.

## Reproducibility

* 2 incidental occurrences during DF-3229 staging (full-rate fstatfs
  hammer crossing `mnt_data=NULL`; identical `fuse_ipc_get+0x63` /
  0x160 signature both times), plus
* the minimal `df3230.c` (mount → open dir fd → umount thread racing a
  fstatfs loop): **panic on the first run** — the NULL window is inside
  the umount syscall, so no timing precision is needed (panic.txt,
  serial_full.txt).

Trust boundary: the umount side needs the mount owner/root (or
usermount policy); the fstatfs side is any process holding an fd on the
mount — a root-triggered unprivileged-user-DoS-able panic in the
standard "user has the fs mounted/opens files while admin unmounts"
scenario.

## Fix (fix.diff, validated)

Guard `fuse_statfs()` and `fuse_statvfs()` entries:

```c
	if (fmp == NULL || fuse_test_dead(fmp))
		return ENOTCONN;
```

Validation: fix (+ DF-3229/3227 fixes) built into the in-guest fuse.ko,
module reloaded, identical PoC rerun → **no panic, guest up** (the race
loop completes; fstatfs now returns ENOTCONN).  Baseline: 3/3 panics on
stock.

## Files

* `df3230.c` — minimal reproducer
* `panic.txt` / `serial_full.txt` — captured serial console (this boot)
* `fix.diff` — validated guard
* `verdict.json` / `manifest.json`

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
