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:
parent
da7496721e
commit
5388625fff
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=224655
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user