⬢ DragonFlyBSD Kernel Audit
DF-5807 / fix.diff
← back to finding ↓ download raw
--- a/sys/vfs/ufs/ufs_vnops.c	2026-06-29 12:51:19.000000000 +0000
+++ ufs_vnops.c	2026-10-13 01:18:09.991161267 +0000
@@ -1654,13 +1654,27 @@
 			eoffset = bp->b_bcount;
 
 		while (offset < eoffset) {
+			uint16_t on_disk_namlen;
 			dp = (struct direct *)(bp->b_data + offset);
 			if (dp->d_reclen <= 0 || (dp->d_reclen & 3) ||
 			    offset + dp->d_reclen > bp->b_bcount) {
 				error = EIO;
 				break;
 			}
-			if (offsetof(struct direct, d_name[dp->d_namlen]) >				     dp->d_reclen) {
+			/*
+			 * DF-5807: in the old on-disk format the real namlen lives
+			 * in the byte read as d_type; the byte read as d_namlen is
+			 * the MSB.  Validate the length that will actually be
+			 * passed to vop_write_dirent() against d_reclen, or a
+			 * crafted image causes an OOB source read.
+			 */
+#if BYTE_ORDER == LITTLE_ENDIAN
+			on_disk_namlen = OFSFMT(vp) ? dp->d_type : dp->d_namlen;
+#else
+			on_disk_namlen = dp->d_namlen;
+#endif
+			if (offsetof(struct direct, d_name[on_disk_namlen]) >
+			    dp->d_reclen) {
 				error = EIO;
 				break;
 			}
@@ -1671,13 +1685,13 @@
 #if BYTE_ORDER == LITTLE_ENDIAN
 			if (OFSFMT(vp)) {
 				retval = vop_write_dirent(&error, uio,
-				    dp->d_ino, dp->d_namlen, dp->d_type,
+				    dp->d_ino, dp->d_namlen, on_disk_namlen,
 				    dp->d_name);
 			} else
 #endif
 			{
 				retval = vop_write_dirent(&error, uio,
-				    dp->d_ino, dp->d_type, dp->d_namlen,
+				    dp->d_ino, dp->d_type, on_disk_namlen,
 				    dp->d_name);
 			}
 			if (retval)