--- 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 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)