#!/bin/sh
#
# Isolated reproducer for the panic in PR 282596, fixed by D58178.
# Run as root on an INVARIANTS kernel.  It panics the machine:
#
#	panic: condition vp->v_type == VDIR || VN_IS_DOOMED(vp) not met
#	       at ../../../kern/vfs_cache.c:NNNN (vn_fullpath_dir)
#	vn_fullpath_dir()
#	vn_fullpath_hardlink()
#	proc_get_binpath()
#	sysctl_kern_proc_pathname()
#
# nullfs can mount a single regular file.  The resulting mount root is a VREG
# vnode with VV_ROOT set, so namei() has no directory to hand back as ni_dvp
# and returns the covered vnode, which for a file mount is itself a regular
# file.  execve(2) stores that as p_textdvp, and any later kern.proc.pathname
# query on the process restarts the path walk from it:
#
#	vn_fullpath_hardlink(vp = mount root, dvp = covered file, "cover")
#	  -> vn_fullpath_dir(covered file)
#	    -> VNPASS(vp->v_type == VDIR || VN_IS_DOOMED(vp), vp)
#
# vn_fullpath_hardlink() only reaches vn_fullpath_dir() once the lock-free
# namecache reverse walk it tries first has failed, so the cover file must not
# be in the namecache.  The rename(2) below evicts it: cache_vop_rename()
# purges the renamed directory, dropping the entries that name its children.
# In the wild (www/foreign-cdm execs its Widevine worker from a single-file
# nullfs mount, then htop or procstat -b is run against it) the entry is
# evicted on its own under namecache pressure.

set -e

kldload -n nullfs

dir=$(mktemp -d)
: >"$dir/cover"                        # file to mount over, one hardlink
mount_nullfs /bin/sleep "$dir/cover"   # single-file nullfs mount

"$dir/cover" 300 &                     # execve() -> p_textdvp = covered file
sleep 1                                # let the exec finish

mv "$dir" "$dir.renamed"               # evict the cover file's cache entry

procstat -b $!                         # kern.proc.pathname -> panic
