⬢ DragonFlyBSD Kernel Audit
DF-3207 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -1243,8 +1243,17 @@
 		error = nlookup(&nd);
 	if (error == 0) {
 		mp = nd.nl_nch.mount;
-		error = VFS_QUOTACTL(mp, uap->cmd, uap->uid,
-				    uap->arg, nd.nl_cred);
+		/*
+		 * Interlock against a concurrent (possibly forced)
+		 * unmount racing the VFS_QUOTACTL() mnt_data deref.
+		 */
+		if (vfs_busy(mp, LK_NOWAIT) == 0) {
+			error = VFS_QUOTACTL(mp, uap->cmd, uap->uid,
+					    uap->arg, nd.nl_cred);
+			vfs_unbusy(mp);
+		} else {
+			error = EBUSY;
+		}
 	}
 	nlookup_done(&nd);
 done:
@@ -1426,6 +1435,17 @@
 	if ((error = nlookup(nd)) != 0)
 		return (error);
 	mp = nd->nl_nch.mount;
+
+	/*
+	 * Interlock against a concurrent (possibly forced) unmount.
+	 * The nlookup result only pins the mount structure, not the
+	 * filesystem-private mnt_data: umount -f is allowed to proceed
+	 * once dounmount()'s retry loop exhausts, freeing mnt_data
+	 * (e.g. nullfs_unmount) while we are between the nlookup and
+	 * the VFS_STATFS() deref.
+	 */
+	if (vfs_busy(mp, LK_NOWAIT))
+		return (EBUSY);
 	sp = &mp->mnt_stat;
 
 	/*
@@ -1434,6 +1454,7 @@
 	 * revokes perms or goes down).
 	 */
 	error = VFS_STATFS(mp, sp, nd->nl_cred);
+	vfs_unbusy(mp);
 	/* ignore error */
 
 	error = mount_path(p, mp, &fullpath, &freepath);
@@ -1504,12 +1525,25 @@
 	}
 
 	/*
+	 * Interlock against a concurrent (possibly forced) unmount.
+	 * The descriptor's nchandle only pins the mount structure,
+	 * not mnt_data, which umount -f may free out from under us
+	 * (e.g. via nullfs_unmount) even though our fp still
+	 * references the mount.
+	 */
+	if (vfs_busy(mp, LK_NOWAIT)) {
+		error = EBUSY;
+		goto done;
+	}
+
+	/*
 	 * Ignore refresh error, user should have visibility.
 	 * This can happen if a NFS mount goes bad (e.g. server
 	 * revokes perms or goes down).
 	 */
 	sp = &mp->mnt_stat;
 	error = VFS_STATFS(mp, sp, fp->f_cred);
+	vfs_unbusy(mp);
 
 	if ((error = mount_path(p, mp, &fullpath, &freepath)) != 0)
 		goto done;