⬢ DragonFlyBSD Kernel Audit
DF-3241 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -417,8 +417,20 @@
 
 	lwkt_gettoken_shared(&mountlist_token);
 	mp = mount_rb_tree_RB_LOOKUP_FSID(&mounttree, fsid);
-	if (mp)
+	/*
+	 * DF-3241: do not hand out mounts being torn down.  dounmount()
+	 * runs VFS_UNMOUNT() (which frees the filesystem-private mount
+	 * data and clears mp->mnt_data) *before* removing the mount from
+	 * the mount list, so a lookup racing that window would return a
+	 * mount whose mnt_data is NULL/stale.  All fsid-based consumers
+	 * (nfsrv_fhtovp, fhopen/fhstat/fhstatfs/fhstatvfs, checkpoint)
+	 * expect a fully-live mount; ESTALE is the correct answer for
+	 * a mount that is on its way out.
+	 */
+	if (mp && (mp->mnt_kern_flag & MNTK_UNMOUNT) == 0)
 		mount_hold(mp);
+	else
+		mp = NULL;
 	lwkt_reltoken(&mountlist_token);
 	return (mp);
 }
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -5018,8 +5018,17 @@
 	if (error)
 		return (error);
 
-	if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL)
+	if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) {
 		error = ESTALE;
+	} else if (vfs_busy(mp, LK_NOWAIT)) {
+		/*
+		 * DF-3241: mount is being unmounted; its mnt_data may
+		 * already be gone.  ESTALE is the correct answer.
+		 */
+		mount_drop(mp);
+		mp = NULL;
+		error = ESTALE;
+	}
 	if (error == 0) {
 		if ((error = VFS_FHTOVP(mp, NULL, &fh.fh_fid, &vp)) == 0) {
 			error = vn_stat(vp, &sb, td->td_ucred);
@@ -5028,8 +5037,10 @@
 	}
 	if (error == 0)
 		error = copyout(&sb, uap->sb, sizeof(sb));
-	if (mp)
+	if (mp) {
+		vfs_unbusy(mp);
 		mount_drop(mp);
+	}
 
 	return (error);
 }
--- a/sys/vfs/nfs/nfs_subs.c
+++ b/sys/vfs/nfs/nfs_subs.c
@@ -1235,12 +1235,24 @@
 	mp = *mpp = vfs_getvfs(&fhp->fh_fsid);
 	if (mp == NULL)
 		return (ESTALE);
+	/*
+	 * DF-3241: hold the mount busy across the CHECKEXP/FHTOVP pair so
+	 * a concurrent dounmount() cannot free mnt_data underneath us.
+	 * vfs_busy() fails once MNTK_UNMOUNT is set.
+	 */
+	if (vfs_busy(mp, LK_NOWAIT)) {
+		mount_drop(mp);
+		*mpp = NULL;
+		return (ESTALE);
+	}
 	error = VFS_CHECKEXP(mp, nam, &exflags, &credanon);
 	if (error) {
+		vfs_unbusy(mp);
 		mount_drop(mp);
 		return (error); 
 	}
 	error = VFS_FHTOVP(mp, NULL, &fhp->fh_fid, vpp);
+	vfs_unbusy(mp);
 	mount_drop(mp);
 	if (error)
 		return (ESTALE);
--- a/sys/vfs/ufs/ufs_vfsops.c
+++ b/sys/vfs/ufs/ufs_vfsops.c
@@ -240,6 +240,8 @@
 	struct ufsmount *ump;
 
 	ump = VFSTOUFS(mp);
+	if (ump == NULL)		/* DF-3241: mid-unmount, mnt_data gone */
+		return (EACCES);
 	/*
 	 * Get the export permission structure for this <mp, client> tuple.
 	 */
--- a/sys/vfs/ufs/ffs_vfsops.c
+++ b/sys/vfs/ufs/ffs_vfsops.c
@@ -1213,6 +1213,8 @@
 	struct fs *fs;
 
 	ufhp = (struct ufid *)fhp;
+	if (VFSTOUFS(mp) == NULL)	/* DF-3241: mid-unmount, mnt_data gone */
+		return (ESTALE);
 	fs = VFSTOUFS(mp)->um_fs;
 	if (ufhp->ufid_ino < UFS_ROOTINO ||
 	    ufhp->ufid_ino >= fs->fs_ncg * fs->fs_ipg)