Do not assume that we own the use reference on the covered vnode until

we set MNTK_UNMOUNT flag on the mp.  Otherwise parallel unmount which
wins race with us could dereference the covered vnode, and we are
left with the locked freed memory.

Reported and tested by:	pho
Sponsored by:	The FreeBSD Foundation
Approved by:	re (gjb)
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2016-06-15 15:56:03 +00:00
parent 2d5bba3ae3
commit 9fdbfd3b6c

View File

@ -1220,7 +1220,6 @@ dounmount(struct mount *mp, int flags, struct thread *td)
VI_LOCK(coveredvp);
vholdl(coveredvp);
vn_lock(coveredvp, LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY);
vdrop(coveredvp);
/*
* Check for mp being unmounted while waiting for the
* covered vnode lock.
@ -1228,18 +1227,22 @@ dounmount(struct mount *mp, int flags, struct thread *td)
if (coveredvp->v_mountedhere != mp ||
coveredvp->v_mountedhere->mnt_gen != mnt_gen_r) {
VOP_UNLOCK(coveredvp, 0);
vdrop(coveredvp);
vfs_rel(mp);
return (EBUSY);
}
}
/*
* Only privileged root, or (if MNT_USER is set) the user that did the
* original mount is permitted to unmount this filesystem.
*/
error = vfs_suser(mp, td);
if (error != 0) {
if (coveredvp)
if (coveredvp != NULL) {
VOP_UNLOCK(coveredvp, 0);
vdrop(coveredvp);
}
vfs_rel(mp);
return (error);
}
@ -1249,8 +1252,10 @@ dounmount(struct mount *mp, int flags, struct thread *td)
if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0 ||
!TAILQ_EMPTY(&mp->mnt_uppers)) {
MNT_IUNLOCK(mp);
if (coveredvp)
if (coveredvp != NULL) {
VOP_UNLOCK(coveredvp, 0);
vdrop(coveredvp);
}
vn_finished_write(mp);
return (EBUSY);
}
@ -1283,6 +1288,16 @@ dounmount(struct mount *mp, int flags, struct thread *td)
if (mp->mnt_flag & MNT_EXPUBLIC)
vfs_setpublicfs(NULL, NULL, NULL);
/*
* From now, we can claim that the use reference on the
* coveredvp is ours, and the ref can be released only by
* successfull unmount by us, or left for later unmount
* attempt. The previously acquired hold reference is no
* longer needed to protect the vnode from reuse.
*/
if (coveredvp != NULL)
vdrop(coveredvp);
vfs_msync(mp, MNT_WAIT);
MNT_ILOCK(mp);
async_flag = mp->mnt_flag & MNT_ASYNC;