Author: kib
Date: Wed May 27 09:22:50 2015
New Revision: 283602
URL: https://svnweb.freebsd.org/changeset/base/283602

Log:
  Right now, dounmount() is called with unreferenced mount point.
  Nothing stops a parallel unmount to suceed before the given call to
  dounmount() checks and locks the covered vnode.  Prevent dounmount()
  from acting on the freed (although type-stable) memory by changing the
  interface to require the mount point to be referenced.  dounmount()
  consumes the reference on return, regardless of the sucessfull or
  erronous result.
  
  Tested by:    pho
  Sponsored by: The FreeBSD Foundation
  MFC after:    2 weeks

Modified:
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
  head/sys/kern/vfs_mount.c
  head/sys/kern/vfs_subr.c

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c    Wed May 
27 09:21:47 2015        (r283601)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c    Wed May 
27 09:22:50 2015        (r283602)
@@ -697,6 +697,7 @@ zfsctl_unmount_snap(zfs_snapentry_t *sep
 
        return (0);
 #else
+       vfs_ref(vn_mountedvfs(svp));
        return (dounmount(vn_mountedvfs(svp), fflags, curthread));
 #endif
 }

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Wed May 
27 09:21:47 2015        (r283601)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c     Wed May 
27 09:22:50 2015        (r283602)
@@ -3481,6 +3481,7 @@ zfs_unmount_snap(const char *snapname)
 #ifdef illumos
        (void) dounmount(vfsp, MS_FORCE, kcred);
 #else
+       vfs_ref(vfsp);
        (void) dounmount(vfsp, MS_FORCE, curthread);
 #endif
        return (0);

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c    Wed May 
27 09:21:47 2015        (r283601)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c    Wed May 
27 09:22:50 2015        (r283602)
@@ -2315,6 +2315,7 @@ bail:
                 * unmount this file system.
                 */
                if (vn_vfswlock(zfsvfs->z_vfs->vfs_vnodecovered) == 0)
+                       vfs_ref(zfsvfs->z_vfs);
                        (void) dounmount(zfsvfs->z_vfs, MS_FORCE, curthread);
        }
        return (err);

Modified: head/sys/kern/vfs_mount.c
==============================================================================
--- head/sys/kern/vfs_mount.c   Wed May 27 09:21:47 2015        (r283601)
+++ head/sys/kern/vfs_mount.c   Wed May 27 09:22:50 2015        (r283602)
@@ -1128,12 +1128,7 @@ struct unmount_args {
 #endif
 /* ARGSUSED */
 int
-sys_unmount(td, uap)
-       struct thread *td;
-       register struct unmount_args /* {
-               char *path;
-               int flags;
-       } */ *uap;
+sys_unmount(struct thread *td, struct unmount_args *uap)
 {
        struct nameidata nd;
        struct mount *mp;
@@ -1164,8 +1159,10 @@ sys_unmount(td, uap)
                mtx_lock(&mountlist_mtx);
                TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
                        if (mp->mnt_stat.f_fsid.val[0] == id0 &&
-                           mp->mnt_stat.f_fsid.val[1] == id1)
+                           mp->mnt_stat.f_fsid.val[1] == id1) {
+                               vfs_ref(mp);
                                break;
+                       }
                }
                mtx_unlock(&mountlist_mtx);
        } else {
@@ -1183,8 +1180,10 @@ sys_unmount(td, uap)
                }
                mtx_lock(&mountlist_mtx);
                TAILQ_FOREACH_REVERSE(mp, &mountlist, mntlist, mnt_list) {
-                       if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0)
+                       if (strcmp(mp->mnt_stat.f_mntonname, pathbuf) == 0) {
+                               vfs_ref(mp);
                                break;
+                       }
                }
                mtx_unlock(&mountlist_mtx);
        }
@@ -1202,8 +1201,10 @@ sys_unmount(td, uap)
        /*
         * Don't allow unmounting the root filesystem.
         */
-       if (mp->mnt_flag & MNT_ROOTFS)
+       if (mp->mnt_flag & MNT_ROOTFS) {
+               vfs_rel(mp);
                return (EINVAL);
+       }
        error = dounmount(mp, uap->flags, td);
        return (error);
 }
@@ -1212,10 +1213,7 @@ sys_unmount(td, uap)
  * Do the actual filesystem unmount.
  */
 int
-dounmount(mp, flags, td)
-       struct mount *mp;
-       int flags;
-       struct thread *td;
+dounmount(struct mount *mp, int flags, struct thread *td)
 {
        struct vnode *coveredvp, *fsrootvp;
        int error;
@@ -1235,6 +1233,7 @@ dounmount(mp, flags, td)
                if (coveredvp->v_mountedhere != mp ||
                    coveredvp->v_mountedhere->mnt_gen != mnt_gen_r) {
                        VOP_UNLOCK(coveredvp, 0);
+                       vfs_rel(mp);
                        return (EBUSY);
                }
        }
@@ -1243,13 +1242,14 @@ dounmount(mp, flags, td)
         * original mount is permitted to unmount this filesystem.
         */
        error = vfs_suser(mp, td);
-       if (error) {
+       if (error != 0) {
                if (coveredvp)
                        VOP_UNLOCK(coveredvp, 0);
+               vfs_rel(mp);
                return (error);
        }
 
-       vn_start_write(NULL, &mp, V_WAIT);
+       vn_start_write(NULL, &mp, V_WAIT | V_MNTREF);
        MNT_ILOCK(mp);
        if ((mp->mnt_kern_flag & MNTK_UNMOUNT) != 0 ||
            !TAILQ_EMPTY(&mp->mnt_uppers)) {

Modified: head/sys/kern/vfs_subr.c
==============================================================================
--- head/sys/kern/vfs_subr.c    Wed May 27 09:21:47 2015        (r283601)
+++ head/sys/kern/vfs_subr.c    Wed May 27 09:22:50 2015        (r283602)
@@ -3502,8 +3502,9 @@ vfs_unmountall(void)
         */
        while(!TAILQ_EMPTY(&mountlist)) {
                mp = TAILQ_LAST(&mountlist, mntlist);
+               vfs_ref(mp);
                error = dounmount(mp, MNT_FORCE, td);
-               if (error) {
+               if (error != 0) {
                        TAILQ_REMOVE(&mountlist, mp, mnt_list);
                        /*
                         * XXX: Due to the way in which we mount the root
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "[email protected]"

Reply via email to