vfs: stop refing freed mount points in vop_stdgetwritemount

The code used blindly ref based on an unsafely red address and then would
backpedal if necessary. This was safe in terms of memory access since
mounts are type-stable, but made for a potential a bug where the mount
was reused and had the count reset to 0 before this code decreased it.

Reviewed by:	kib
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D21411
This commit is contained in:
Mateusz Guzik 2019-09-01 14:01:09 +00:00
parent b21097894e
commit 2796c209b0

View File

@ -588,22 +588,28 @@ vop_stdgetwritemount(ap)
} */ *ap;
{
struct mount *mp;
struct vnode *vp;
/*
* XXX Since this is called unlocked we may be recycled while
* attempting to ref the mount. If this is the case or mountpoint
* will be set to NULL. We only have to prevent this call from
* returning with a ref to an incorrect mountpoint. It is not
* harmful to return with a ref to our previous mountpoint.
* Note that having a reference does not prevent forced unmount from
* setting ->v_mount to NULL after the lock gets released. This is of
* no consequence for typical consumers (most notably vn_start_write)
* since in this case the vnode is VI_DOOMED. Unmount might have
* progressed far enough that its completion is only delayed by the
* reference obtained here. The consumer only needs to concern itself
* with releasing it.
*/
mp = ap->a_vp->v_mount;
if (mp != NULL) {
vfs_ref(mp);
if (mp != ap->a_vp->v_mount) {
vfs_rel(mp);
mp = NULL;
}
vp = ap->a_vp;
mp = vp->v_mount;
MNT_ILOCK(mp);
if (mp != vp->v_mount) {
MNT_IUNLOCK(mp);
mp = NULL;
goto out;
}
MNT_REF(mp);
MNT_IUNLOCK(mp);
out:
*(ap->a_mpp) = mp;
return (0);
}