⬢ DragonFlyBSD Kernel Audit
DF-3116 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/msdosfs/msdosfs_lookup.c	2026-09-06 14:46:59.918457332 +0000
+++ b/sys/vfs/msdosfs/msdosfs_lookup.c	2026-09-06 14:46:59.942457024 +0000
@@ -625,6 +625,46 @@
 	}
 
 	/*
+	 * DF-3116: de_fndoffset/de_fndcnt were recorded by an earlier
+	 * msdosfs_lookup() which may have run while the directory was
+	 * unlocked (msdosfs_rename() drops tdvp across doscheckpath() and
+	 * across its source relookup(), then restores the saved values at
+	 * vnops:1186).  A concurrent create may have consumed the slot in
+	 * the meantime; writing it blindly clobbers a live dirent.
+	 * Re-validate that every slot in [fndoffset - fndcnt*32, fndoffset]
+	 * is still free (never used or deleted) before writing anything.
+	 */
+	{
+		u_long voff = ddep->de_fndoffset;
+		struct buf *vbp;
+		struct direntry *vep;
+		int k;
+
+		for (k = 0;; k++) {
+			error = pcbmap(ddep, de_cluster(pmp, voff),
+			    &bn, &dirclust, &blsize);
+			if (error)
+				return (error);
+			error = bread(pmp->pm_devvp, de_bn2doff(pmp, bn),
+			    blsize, &vbp);
+			if (error != 0) {
+				brelse(vbp);
+				return (error);
+			}
+			vep = bptoep(pmp, vbp, voff);
+			if (vep->deName[0] != SLOT_EMPTY &&
+			    vep->deName[0] != SLOT_DELETED) {
+				brelse(vbp);
+				return (EEXIST);	/* slot stolen from us */
+			}
+			brelse(vbp);
+			if (k >= ddep->de_fndcnt || voff < sizeof(struct direntry))
+				break;
+			voff -= sizeof(struct direntry);
+		}
+	}
+
+	/*
 	 * We just read in the cluster with space.  Copy the new directory
 	 * entry in.  Then write it to disk. NOTE:  DOS directories
 	 * do not get smaller as clusters are emptied.