Author: mm
Date: Fri Aug  5 11:12:50 2011
New Revision: 224655
URL: http://svn.freebsd.org/changeset/base/224655

Log:
  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 <da...@catwhisker.org>
  Reviewed by:  kib
  Approved by:  re (bz)
  MFC after:    2 weeks

Modified:
  head/sys/kern/vfs_mount.c

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c   Fri Aug  5 06:57:44 2011        (r224654)
+++ head/sys/kern/vfs_mount.c   Fri Aug  5 11:12:50 2011        (r224655)
@@ -362,6 +362,60 @@ vfs_mergeopts(struct vfsoptlist *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.
  */
 int
@@ -745,6 +799,7 @@ static int
 vfs_domount_first(
        struct thread *td,              /* Calling thread. */
        struct vfsconf *vfsp,           /* File system type. */
+       char *fspath,                   /* Mount path. */
        struct vnode *vp,               /* Vnode to be covered. */
        int fsflags,                    /* Flags common to all filesystems. */
        struct vfsoptlist **optlist     /* Options local to the filesystem. */
@@ -753,25 +808,12 @@ vfs_domount_first(
        struct vattr va;
        struct mount *mp;
        struct vnode *newdp;
-       char *fspath, *fbuf;
        int error;
 
        mtx_assert(&Giant, MA_OWNED);
        ASSERT_VOP_ELOCKED(vp, __func__);
        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
         * onto which we are attempting to mount.
@@ -793,14 +835,12 @@ vfs_domount_first(
        }
        if (error != 0) {
                vput(vp);
-               free(fbuf, M_TEMP);
                return (error);
        }
        VOP_UNLOCK(vp, 0);
 
        /* Allocate and initialize the filesystem. */
        mp = vfs_mount_alloc(vp, vfsp, fspath, td->td_ucred);
-       free(fbuf, M_TEMP);
        /* XXXMAC: pass to vfs_mount_alloc? */
        mp->mnt_optnew = *optlist;
        /* Set the mount level flags. */
@@ -1083,15 +1123,15 @@ vfs_domount(
                mtx_lock(&Giant);
        NDFREE(&nd, NDF_ONLY_PNBUF);
        vp = nd.ni_vp;
-       if ((fsflags & MNT_UPDATE) == 0)
-               error = vfs_domount_first(td, vfsp, vp, fsflags, optlist);
-       else
+       if ((fsflags & MNT_UPDATE) == 0) {
+               error = vfs_verify_global_path(td, vp, fspath);
+               if (error == 0)
+                       error = vfs_domount_first(td, vfsp, fspath, vp,
+                           fsflags, optlist);
+       } else
                error = vfs_domount_update(td, vp, fsflags, optlist);
        mtx_unlock(&Giant);
 
-       ASSERT_VI_UNLOCKED(vp, __func__);
-       ASSERT_VOP_UNLOCKED(vp, __func__);
-
        return (error);
 }
 
@@ -1118,7 +1158,7 @@ unmount(td, uap)
 {
        struct mount *mp;
        struct nameidata nd;
-       char *pathbuf, *rpathbuf, *fbuf;
+       char *pathbuf;
        int error, id0, id1;
 
        AUDIT_ARG_VALUE(uap->flags);
@@ -1163,16 +1203,13 @@ unmount(td, uap)
                            FOLLOW | LOCKLEAF | MPSAFE | AUDITVNODE1,
                            UIO_SYSSPACE, pathbuf, td);
                        if (namei(&nd) == 0) {
+                               if (NDHASGIANT(&nd))
+                                       mtx_unlock(&Giant);
                                NDFREE(&nd, NDF_ONLY_PNBUF);
-                               if (vn_fullpath_global(td, nd.ni_vp, &rpathbuf,
-                                   &fbuf) == 0) {
-                                       if (strlen(rpathbuf) < MNAMELEN) {
-                                               strlcpy(pathbuf, rpathbuf,
-                                                   MNAMELEN);
-                                       }
-                                       free(fbuf, M_TEMP);
-                               }
-                               vput(nd.ni_vp);
+                               error = vfs_verify_global_path(td, nd.ni_vp,
+                                   pathbuf);
+                               if (error == 0)
+                                       vput(nd.ni_vp);
                        }
                }
                mtx_lock(&mountlist_mtx);
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to