For file mounts, lookup returns the mount root vnode with ni_dvp set
to vp_crossmp, which cannot be used to walk up the directory tree.
execve(2) of such a file stores vp_crossmp as p_textdvp, and a later
kern.proc.pathname query on the process descends into it, tripping
VNPASS(vp->v_type == VDIR || VN_IS_DOOMED(vp)) in vn_fullpath_dir()
on INVARIANTS kernels. This is readily triggered by www/foreign-cdm,
which execs its Widevine worker from a single-file nullfs mount: any
subsequent htop or procstat -b against that process panics the box.
Resolve file mounts in vn_fullpath_hardlink() itself via the covered
vnode, the same way a1d74b2dab78 handled realpath(2); the resulting
path is unambiguous since file mounts require a link count of 1. This
covers all callers: kern.proc.pathname, AT_EXECPATH for a relative
execve of a file mount, and vn_path_to_global_path_hardlink(). Drop
the now-redundant special case in kern___realpathat().
Since vn_fullpath() resolves into its own MAXPATHLEN buffer, check the
result against the caller-supplied size and return ENAMETOOLONG when
it does not fit, as the dropped special case did via the check in
kern___realpathat(); without this, __realpathat(2) would copy out past
the size given by the caller. Take the branch before the size
validation and work buffer allocation so that undersized buffers keep
returning ENAMETOOLONG rather than EINVAL and the work buffer is not
allocated just to be freed.
PR: 282596
Signed-off-by: Nick Price <nick@spun.io>