vn_fullpath1() checked VV_ROOT and then unreferenced

vp->v_mount->mnt_vnodecovered unlocked.  This allowed unmount to race.
Lock vnode after we noticed the VV_ROOT flag.  See comments for
explanation why unlocked check for the flag is considered safe.

Reported and tested by:	avg
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2016-11-07 10:55:56 +00:00
parent 921081191d
commit 9bd4f0a2c6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=308407

View File

@ -2245,17 +2245,35 @@ vn_fullpath1(struct thread *td, struct vnode *vp, struct vnode *rdir,
slash_prefixed = 1;
}
while (vp != rdir && vp != rootvnode) {
if (vp->v_vflag & VV_ROOT) {
if (vp->v_iflag & VI_DOOMED) { /* forced unmount */
vrele(vp);
/*
* The vp vnode must be already fully constructed,
* since it is either found in namecache or obtained
* from VOP_VPTOCNP(). We may test for VV_ROOT safely
* without obtaining the vnode lock.
*/
if ((vp->v_vflag & VV_ROOT) != 0) {
vn_lock(vp, LK_RETRY | LK_SHARED);
/*
* With the vnode locked, check for races with
* unmount, forced or not. Note that we
* already verified that vp is not equal to
* the root vnode, which means that
* mnt_vnodecovered can be NULL only for the
* case of unmount.
*/
if ((vp->v_iflag & VI_DOOMED) != 0 ||
(vp1 = vp->v_mount->mnt_vnodecovered) == NULL ||
vp1->v_mountedhere != vp->v_mount) {
vput(vp);
error = ENOENT;
SDT_PROBE3(vfs, namecache, fullpath, return,
error, vp, NULL);
break;
}
vp1 = vp->v_mount->mnt_vnodecovered;
vref(vp1);
vrele(vp);
vput(vp);
vp = vp1;
continue;
}