# DF-3061 — NFS rename retry corrupts/leaks namecache state → remote kernel panic

## What

`nfs_namei()` (sys/vfs/nfs/nfs_subs.c:1003-1011) supports being called a
second time on the *same* `struct nlookupdata` ("retry case"), but only
rescues the path buffer.  The subsequent `nlookup_init_raw()` (called at
nfs_subs.c:1155) unconditionally `bzero()`s the whole `nd`, silently
discarding live state: the **exclusively-locked leaf ncp** (`nl_nch`,
`NLC_NCPISLOCKED`), its cache reference, the `nl_rootnch`/`nl_jailnch`
references and a `crhold()`'d `nl_cred` (see `nlookup_init_raw`,
sys/kern/vfs_nlookup.c:255-292, and `nlookup_done`, :366-400 for the
release protocol that is being skipped).

The only in-tree caller that uses the retry is `nfsrv_rename()`
(sys/vfs/nfs/nfs_serv.c:2255-2271): when the namecache topology changes
between the two `nfs_namei()` lookups and `cache_lock4_tondlocked()`
revalidation, it does `cache_put(&fnchd); cache_put(&tnchd); goto again;`.
The retry branch additionally leaves `fnchd_status`/`tnchd_status == 2`
and stale `fdirp`/`tdirp` pointers, so a failure of the *second*
iteration's lookups drives the `nfsmout:` cleanup (nfs_serv.c:2382-2391)
to `cache_unlock`/`cache_drop` already-released handles and `vrele` an
already-vrele'd directory vnode — double-drop / refcount-underflow /
use-after-free.

## Impact

* Observed on the stock INVARIANTS guest: **kernel panic** in the nfsd
  process immediately after the `nfs - retry rename` console message —
  `Fatal trap 12 ... lockmgr_release+0x11`, fault at 0xc8 (a NULL ncp's
  `nc_lock` at offset 0xc0 in `struct namecache`). 2/2 runs, ~2-2.5 min
  of racing each.
* Even without the crash: leaked exclusive ncp locks make every later
  lookup of the affected names block forever; leaked cache/cred refs are
  unbounded.
* Reachable by any NFS client permitted by the server's exports, with
  ordinary RENAME RPCs racing concurrent modifications of the same names
  (a local racer on the exported tree or a second client). No
  authentication beyond the export ACL is required.

## Files

* `racer.c`  — server-side churn racer (rename/unlink loop on src/dst)
* `renamer.c`, `cl.c` (built in-guest) — client-side RENAME hammer
* `panic.txt`, `panic_run2.txt` — the two captured panics
* `fix.diff` — the verified fix (nfs_subs.c retry teardown + nfs_serv.c
  status/pointer reset)
* `build.log`, `run.log` — fix-validation kernel build and race run

## Reproduce

In the guest (NFS server + loopback client):

```sh
mkdir -p /export /mnt/nfs
echo "/export -maproot=root localhost" > /etc/exports
rpcbind; /sbin/nfsd -t -u -n 4; sleep 1; /sbin/mountd & sleep 2
mount localhost:/export /mnt/nfs
# build racer + cl (see run.sh), then:
( /tmp/rc4 /export & ) ; ( for i in 1 2 3 4 5 6; do /tmp/cl /mnt/nfs & done )
# within ~1-3 min: console shows
#   nfs - retry rename src to dst
#   Fatal trap 12 ... lockmgr_release+0x11  (kernel panic)
```

Success criterion (baseline): panic with the signature above within
minutes. Patched: the retry message may still appear (it did, 8x), but
the system stays up with no trap/panic (see run.log for the observed
post-race umount caveat, which could not be compared against baseline).
