DragonFlyBSD Kernel Audit
DF-0803 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/ext2fs/ext2_vfsops.c b/sys/vfs/ext2fs/ext2_vfsops.c
--- a/sys/vfs/ext2fs/ext2_vfsops.c
+++ b/sys/vfs/ext2fs/ext2_vfsops.c
@@ -617,12 +617,24 @@
 		return (EINVAL);
 	}
 
-	fs->e2fs_gcount = howmany(fs->e2fs_bcount -
-	    le32toh(es->e2fs_first_dblock), EXT2_BLOCKS_PER_GROUP(fs));
-	if (fs->e2fs_gcount > ((uint64_t)1 << 32) - EXT2_DESCS_PER_BLOCK(fs)) {
-		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
-		    "groups count too large");
-		return (EINVAL);
+	{
+		/*
+		 * DF-0803: compute gcount as uint64_t FIRST, validate before
+		 * the narrowing assignment to uint32_t e2fs_gcount. The old
+		 * code truncated via the assignment and then compared the
+		 * already-truncated value against ~2^32, which only caught
+		 * the top 64 uint32 values.
+		 */
+		uint64_t gcount64 = howmany(fs->e2fs_bcount -
+		    le32toh(es->e2fs_first_dblock), EXT2_BLOCKS_PER_GROUP(fs));
+
+		if (gcount64 == 0 ||
+		    gcount64 > ((uint64_t)1 << 32) - EXT2_DESCS_PER_BLOCK(fs)) {
+			SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
+			    "groups count too large");
+			return (EINVAL);
+		}
+		fs->e2fs_gcount = (uint32_t)gcount64;
 	}
 
 	/* Check for extra isize in big inodes. */
@@ -1349,10 +1361,17 @@
 restart:
 	if ((*vpp = ext2_ihashget(ump->um_dev, ino)) != NULL)
 		return (0);
+	/*
+	 * DF-0803: bounds-check the inode's group against e2fs_gcount before
+	 * any e2fs_gd[ino_to_cg(...)] dereference. Without this, a corrupted
+	 * (or maliciously crafted) image can drive an out-of-bounds GD access.
+	 */
+	fs = ump->um_e2fs;
+	if (ino < EXT2_ROOTINO || ino_to_cg(fs, ino) >= fs->e2fs_gcount)
+		return (EINVAL);
 	if (ext2_alloc_vnode(mp, ino, &vp) == -1)
 		goto restart;
 	ip = VTOI(vp);
-	fs = ip->i_e2fs;
 
 	/* Read in the disk contents for the inode, copy into the inode. */
 	if ((error = bread(ump->um_devvp, fsbtodoff(fs, ino_to_fsba(fs, ino)),