⬢ DragonFlyBSD Kernel Audit
DF-3106 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/ufs/ufs_lookup.c
+++ b/sys/vfs/ufs/ufs_lookup.c
@@ -808,6 +808,26 @@
 	for (loc = ep->d_reclen; loc < dp->i_count; ) {
 		nep = (struct direct *)(dirbuf + loc);
 
+		/*
+		 * The compaction region was validated by ufs_lookup() for
+		 * forward progress only (d_reclen != 0 and fitting in the
+		 * remaining DIRBLKSIZ space); with debug.dircheck == 0 the
+		 * d_namlen was never checked, so DIRSIZ(nep) can exceed
+		 * nep->d_reclen on a corrupted filesystem image.  Compacting
+		 * such an entry copies DIRSIZ(nep) bytes and advances the
+		 * destination pointer by that amount each iteration, walking
+		 * far past the end of the directory buffer (kernel heap OOB
+		 * write, DF-3106).  Refuse corrupted regions instead.
+		 */
+		if (nep->d_reclen == 0 ||
+		    nep->d_reclen > dp->i_count - loc ||
+		    (nep->d_ino != 0 &&
+		     nep->d_reclen < DIRSIZ(OFSFMT(dvp), nep))) {
+			bp->b_flags |= B_INVAL;
+			brelse(bp);
+			return (EIO);
+		}
+
 		/* Trim the existing slot (NB: dsize may be zero). */
 		ep->d_reclen = dsize;
 		ep = (struct direct *)((char *)ep + dsize);