# DF-3117 VERDICT — racing create vs cross-parent dir rename: mount-ref leak → umount hang → system wedge

**Status: REPRODUCED (impact=dos).  Twice; the decisive run is
attribution-clean.**

## How it reproduces

1. Fresh `vm.sh reset with-src` guest (stock INVARIANTS kernel #0,
   6 vCPU), fresh 512 MB FAT32 image on /dev/vn0, mounted
   `-u 65534 -g 65534 -m 0777` at /fatrace.
2. p1 = /fatrace/p1 (shallow); p2 = /fatrace/deep/l1/../l100 (a
   100-deep chain, so every `doscheckpath()` walk is ~100 hops).
3. Three unprivileged (nobody) racers for 30 s:
   - bouncer: 38,661 cross-parent directory renames (mkdir D<k> in p1,
     rename into p2) — all succeeded (0 errors),
   - creator in p2: 58,608 create+verify+rotate cycles — 0 vanished,
   - creator in p1: 41,422 cycles — 0 vanished.
4. All racers exit; `ps` confirms **zero leftover processes**
   ("leftover procs: none").
5. `umount /fatrace` under a 20 s watchdog **never completes**; the ssh
   channel then dies ("Connection timed out during banner exchange"),
   the serial console freezes (no further output, no panic, no DDB),
   and `vm.sh status` reports down while the QEMU process stays alive.
   See `run.10.wedge.log` (cut off mid-umount by the wedge itself) and
   `run.9.log` for the first occurrence.

First occurrence (run.9, same race shape, 60 s): identical — umount
stage hung silently, guest later went unresponsive; the console from
that session additionally showed the kernel diagnostic
`unmount(/fatrace): ... 2 mount refs still present` (from a
subsequent forced umount attempt), i.e. **two leaked mount
references**, and `kern_rename: retry due to race on: "D68634" ->
"D68634" (12,0)` (sys/kern/vfs_syscalls.c:4323), proving the rename
race machinery was live during the churn.

## Control (isolates the trigger)

The Window-B configuration — identical creator churn against same-dir
*file* renames (no directory renames, hence no `doscheckpath`) —
unmounts instantly and fsck's clean (run.3.log / run.5.log stage [6]).
The wedge therefore requires the **cross-parent directory rename**
path (msdosfs_rename's doscheckpath/relookup unlock windows +
deget/vput ancestor walk), racing ordinary creates.

## Root cause status

The leaking statement is **not line-pinned**.  Sequentially, the ref
protocol in msdosfs_rename/doscheckpath/vop_compat_nrename is balanced
on every path I hand-traced (including doscheckpath's brelse/deget
error contract — deget clears *depp on all error returns,
msdosfs_denode.c:291,318,374).  The leak therefore manifests only
under a specific interleaving (candidates: the vref(tdvp)+doscheckpath
vput pairing under `kern_rename` EAGAIN retry, the relookup-found-tvp
combinations at vnops:1056-1102, or deget's error-path denode left
hash-resident at msdosfs_denode.c:363-376).  Pinning it requires a
VFS refcount-debug kernel build; recommended first step upstream.

## Impact

Any local user with write access to a single directory on *any*
mounted msdosfs filesystem (USB stick, FAT partition, user-mounted
image with vfs.usermount=1) can, in ~30 s of unprivileged racing,
leak mount references such that the filesystem can never be unmounted
and the system subsequently wedges entirely (no panic, no dump —
hard catatonia).  Availability-only; confidentiality/integrity
untouched by this aspect (the integrity consequence of the same racing
surface is DF-3116).

## Fix

No fix.diff — no line-pinned root cause.  Upstream should reproduce
under refcount instrumentation (e.g. DEBUG_VFS_LOCKS / VFS_REFCOUNT
equivalents) on the bouncer+creator workload in `wedge.sh`; prime
suspects listed above.  fix_status=not_testable for the same reason.

## Guest state

The wedge dirtied the guest twice; `vm.sh reset with-src` was run
afterward and the guest is up and clean (source tree verified restored,
no leftover artifacts beyond /tmp copies removed by the reset).

## 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.

(No fix.diff for this item: documented recommendation only - see Fix above.)
