DF-3207 / fix.diff
--- 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; |