diff --git a/sys/vfs/msdosfs/msdosfs_denode.c b/sys/vfs/msdosfs/msdosfs_denode.c --- a/sys/vfs/msdosfs/msdosfs_denode.c 2026-09-07 09:04:26.755369941 +0000 +++ b/sys/vfs/msdosfs/msdosfs_denode.c 2026-09-07 09:04:26.763369843 +0000 @@ -305,6 +305,15 @@ * Insert the denode into the hash queue. If a collision occurs * throw away the vnode and try again. */ + /* + * The denode must become visible in the hash with its reference + * already held. msdosfs_hashget() treats de_refcnt == 0 entries as + * absent, so if this store happened after the insertion a concurrent + * deget() on another CPU could insert a duplicate denode for the + * same directory entry (verified: duplicate denodes / divergent file + * state under parallel open()). + */ + ldep->de_refcnt = 1; error = msdosfs_hashins(ldep); if (error == EBUSY) { nvp->v_type = VBAD; @@ -320,7 +329,6 @@ } nvp->v_data = ldep; ldep->de_pmp = pmp; - ldep->de_refcnt = 1; /* * Copy the directory entry into the denode area of the vnode. */ diff --git a/sys/vfs/msdosfs/msdosfs_vfsops.c b/sys/vfs/msdosfs/msdosfs_vfsops.c --- a/sys/vfs/msdosfs/msdosfs_vfsops.c 2026-09-07 09:04:26.755369941 +0000 +++ b/sys/vfs/msdosfs/msdosfs_vfsops.c 2026-09-07 09:23:45.340807394 +0000 @@ -853,6 +853,45 @@ struct denode *dep; int error; + /* + * The file handle arrives from an untrusted RPC payload (or any + * fhopen(2)/fhstat(2) caller). deget()->readep() locates the on-disk + * direntry from these fields with no further validation: + * bptoep() computes the entry pointer as bp->b_data + + * (dirofs & pm_crbomask) while the block number calculation ignores + * dirofs entirely, so out-of-range values produce out-of-bounds + * accesses past kernel buffers (verified: fatal page fault in + * DE_INTERNALIZE from a forged NFS GETATTR) and arbitrary + * device-offset reads/writes via stat()/SETATTR (verified). + * Reject anything that cannot name a directory entry that lies + * entirely inside one buffer. + */ + if (fhp->fid_len != sizeof(struct defid)) + return (EINVAL); + if (defhp->defid_dirclust == MSDOSFSROOT || + (FAT32(pmp) && defhp->defid_dirclust == pmp->pm_rootdirblk)) { + /* root directory */ + if (defhp->defid_dirofs == MSDOSFSROOT_OFS) + goto valid; + if (FAT32(pmp)) + return (EINVAL); /* root is a single chain */ + if (defhp->defid_dirofs > + pmp->pm_rootdirsize * DEV_BSIZE - sizeof(struct direntry)) + return (EINVAL); + } else { + /* ordinary cluster directory */ + if (defhp->defid_dirclust < CLUST_FIRST || + defhp->defid_dirclust > pmp->pm_maxcluster) + return (EINVAL); + if (defhp->defid_dirofs > + pmp->pm_bpcluster - sizeof(struct direntry)) + return (EINVAL); + } + /* entry must not cross the end of the buffer it is read from */ + if ((defhp->defid_dirofs & pmp->pm_crbomask) > + pmp->pm_bpcluster - sizeof(struct direntry)) + return (EINVAL); +valid: error = deget(pmp, defhp->defid_dirclust, defhp->defid_dirofs, &dep); if (error) { *vpp = NULLVP;