1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-21 11:13:30 +00:00

The change in r224615 didn't take into account that vn_fullpath_global()

doesn't operate on locked vnode. This could cause a panic.

Fix by unlocking vnode, re-locking afterwards and verifying that it wasn't
renamed or deleted. To improve readability and reduce code size, move code
to a new static function vfs_verify_global_path().

In addition, fix missing giant unlock in unmount().

Reported by:	David Wolfskill <david@catwhisker.org>
Reviewed by:	kib
Approved by:	re (bz)
MFC after:	2 weeks
This commit is contained in:
Martin Matuska 2011-08-05 11:12:50 +00:00
parent da7496721e
commit 5388625fff
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=224655

View File

@ -361,6 +361,60 @@ vfs_mergeopts(struct vfsoptlist *toopts, struct vfsoptlist *oldopts)
vfs_sanitizeopts(toopts); vfs_sanitizeopts(toopts);
} }
/*
* Verify vnode's global path
*/
static int
vfs_verify_global_path(struct thread *td, struct vnode *vp, char *fspath)
{
struct nameidata nd;
struct vnode *vp1;
char *rpath, *fbuf;
int error;
ASSERT_VOP_ELOCKED(vp, __func__);
/* Construct global filesystem path from vp. */
VOP_UNLOCK(vp, 0);
error = vn_fullpath_global(td, vp, &rpath, &fbuf);
if (error != 0) {
vrele(vp);
return (error);
}
if (strlen(rpath) >= MNAMELEN) {
vrele(vp);
error = ENAMETOOLONG;
goto out;
}
/*
* Re-lookup the vnode by path. As a side effect, the vnode is
* relocked. If vnode was renamed, return ENOENT.
*/
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
UIO_SYSSPACE, fspath, td);
error = namei(&nd);
if (error != 0) {
vrele(vp);
goto out;
}
if (NDHASGIANT(&nd))
mtx_unlock(&Giant);
NDFREE(&nd, NDF_ONLY_PNBUF);
vp1 = nd.ni_vp;
vrele(vp);
if (vp1 != vp) {
vput(vp1);
error = ENOENT;
goto out;
}
strlcpy(fspath,rpath,MNAMELEN);
out:
free(fbuf, M_TEMP);
return (error);
}
/* /*
* Mount a filesystem. * Mount a filesystem.
*/ */
@ -745,6 +799,7 @@ static int
vfs_domount_first( vfs_domount_first(
struct thread *td, /* Calling thread. */ struct thread *td, /* Calling thread. */
struct vfsconf *vfsp, /* File system type. */ struct vfsconf *vfsp, /* File system type. */
char *fspath, /* Mount path. */
struct vnode *vp, /* Vnode to be covered. */ struct vnode *vp, /* Vnode to be covered. */
int fsflags, /* Flags common to all filesystems. */ int fsflags, /* Flags common to all filesystems. */
struct vfsoptlist **optlist /* Options local to the filesystem. */ struct vfsoptlist **optlist /* Options local to the filesystem. */
@ -753,25 +808,12 @@ vfs_domount_first(
struct vattr va; struct vattr va;
struct mount *mp; struct mount *mp;
struct vnode *newdp; struct vnode *newdp;
char *fspath, *fbuf;
int error; int error;
mtx_assert(&Giant, MA_OWNED); mtx_assert(&Giant, MA_OWNED);
ASSERT_VOP_ELOCKED(vp, __func__); ASSERT_VOP_ELOCKED(vp, __func__);
KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here")); KASSERT((fsflags & MNT_UPDATE) == 0, ("MNT_UPDATE shouldn't be here"));
/* Construct global filesystem path from vp. */
error = vn_fullpath_global(td, vp, &fspath, &fbuf);
if (error != 0) {
vput(vp);
return (error);
}
if (strlen(fspath) >= MNAMELEN) {
vput(vp);
free(fbuf, M_TEMP);
return (ENAMETOOLONG);
}
/* /*
* If the user is not root, ensure that they own the directory * If the user is not root, ensure that they own the directory
* onto which we are attempting to mount. * onto which we are attempting to mount.
@ -793,14 +835,12 @@ vfs_domount_first(
} }
if (error != 0) { if (error != 0) {
vput(vp); vput(vp);
free(fbuf, M_TEMP);
return (error); return (error);
} }
VOP_UNLOCK(vp, 0); VOP_UNLOCK(vp, 0);
/* Allocate and initialize the filesystem. */ /* Allocate and initialize the filesystem. */
mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred); mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred);
free(fbuf, M_TEMP);
/* XXXMAC: pass to vfs_mount_alloc? */ /* XXXMAC: pass to vfs_mount_alloc? */
mp->mnt_optnew = *optlist; mp->mnt_optnew = *optlist;
/* Set the mount level flags. */ /* Set the mount level flags. */
@ -1083,15 +1123,15 @@ vfs_domount(
mtx_lock(&Giant); mtx_lock(&Giant);
NDFREE(&nd, NDF_ONLY_PNBUF); NDFREE(&nd, NDF_ONLY_PNBUF);
vp = nd.ni_vp; vp = nd.ni_vp;
if ((fsflags & MNT_UPDATE) == 0) if ((fsflags & MNT_UPDATE) == 0) {
error = vfs_domount_first(td, vfsp, vp, fsflags, optlist); error = vfs_verify_global_path(td, vp, fspath);
else if (error == 0)
error = vfs_domount_first(td, vfsp, fspath, vp,
fsflags, optlist);
} else
error = vfs_domount_update(td, vp, fsflags, optlist); error = vfs_domount_update(td, vp, fsflags, optlist);
mtx_unlock(&Giant); mtx_unlock(&Giant);
ASSERT_VI_UNLOCKED(vp, __func__);
ASSERT_VOP_UNLOCKED(vp, __func__);
return (error); return (error);
} }
@ -1118,7 +1158,7 @@ unmount(td, uap)
{ {
struct mount *mp; struct mount *mp;
struct nameidata nd; struct nameidata nd;
char *pathbuf, *rpathbuf, *fbuf; char *pathbuf;
int error, id0, id1; int error, id0, id1;
AUDIT_ARG_VALUE(uap->flags); AUDIT_ARG_VALUE(uap->flags);
@ -1163,16 +1203,13 @@ unmount(td, uap)
FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1, FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
UIO_SYSSPACE, pathbuf, td); UIO_SYSSPACE, pathbuf, td);
if (namei(&nd) == 0) { if (namei(&nd) == 0) {
if (NDHASGIANT(&nd))
mtx_unlock(&Giant);
NDFREE(&nd, NDF_ONLY_PNBUF); NDFREE(&nd, NDF_ONLY_PNBUF);
if (vn_fullpath_global(td, nd.ni_vp, &rpathbuf, error = vfs_verify_global_path(td, nd.ni_vp,
&fbuf) == 0) { pathbuf);
if (strlen(rpathbuf) < MNAMELEN) { if (error == 0)
strlcpy(pathbuf, rpathbuf, vput(nd.ni_vp);
MNAMELEN);
}
free(fbuf, M_TEMP);
}
vput(nd.ni_vp);
} }
} }
mtx_lock(&mountlist_mtx); mtx_lock(&mountlist_mtx);