DragonFlyBSD Kernel Audit
DF-0876 / 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
@@ -546,11 +546,27 @@
 	}
 
 	/* Check group descriptors */
-	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT) &&
-	    le16toh(es->e3fs_desc_size) != E2FS_64BIT_GD_SIZE) {
-		SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
-		    "unsupported 64bit descriptor size");
-		return (EINVAL);
+	if (EXT2_HAS_INCOMPAT_FEATURE(fs, EXT2F_INCOMPAT_64BIT)) {
+		if (le16toh(es->e3fs_desc_size) != E2FS_64BIT_GD_SIZE) {
+			SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
+			    "unsupported 64bit descriptor size");
+			return (EINVAL);
+		}
+	} else {
+		/*
+		 * DF-0876: for non-64bit filesystems, s_desc_size must be 0
+		 * (rev0 default of E2FS_REV0_GD_SIZE bytes) or exactly
+		 * E2FS_REV0_GD_SIZE.  Any other value drives an out-of-bounds
+		 * read in ext2_gd_csum() (ext2_csum.c:684-686), since the
+		 * checksum loop reads `s_desc_size - offset` bytes from the
+		 * in-memory group descriptor.
+		 */
+		if (le16toh(es->e3fs_desc_size) != 0 &&
+		    le16toh(es->e3fs_desc_size) != E2FS_REV0_GD_SIZE) {
+			SDT_PROBE1(ext2fs, , vfsops, ext2_compute_sb_data_error,
+			    "invalid group descriptor size");
+			return (EINVAL);
+		}
 	}
 
 	fs->e2fs_bpg = le32toh(es->e2fs_bpg);
diff --git a/sys/vfs/ext2fs/ext2_csum.c b/sys/vfs/ext2fs/ext2_csum.c
--- a/sys/vfs/ext2fs/ext2_csum.c
+++ b/sys/vfs/ext2fs/ext2_csum.c
@@ -681,9 +681,23 @@
 		csum32 = calculate_crc32c(csum32, (uint8_t *)&dummy_csum,
 		    sizeof(dummy_csum));
 		offset += sizeof(dummy_csum);
-		if (offset < le16toh(fs->e2fs->e3fs_desc_size))
-			csum32 = calculate_crc32c(csum32, (uint8_t *)gd + offset,
-			    le16toh(fs->e2fs->e3fs_desc_size) - offset);
+		/*
+		 * DF-0876: clamp the descriptor checksum read to the size of
+		 * the in-memory group descriptor.  On-disk s_desc_size is
+		 * attacker-controlled and otherwise unchecked for non-64bit
+		 * filesystems; an attacker-chosen value of 0xFFFF would read
+		 * 65503 bytes past the 64-byte struct ext2_gd in the e2fs_gd
+		 * slab allocation.  Mount-time validation (vfsops.c) rejects
+		 * bogus sizes; this clamp is defense-in-depth.
+		 */
+		if (offset < le16toh(fs->e2fs->e3fs_desc_size)) {
+			size_t csum_len =
+			    le16toh(fs->e2fs->e3fs_desc_size) - offset;
+			if (offset + csum_len > sizeof(struct ext2_gd))
+				csum_len = sizeof(struct ext2_gd) - offset;
+			csum32 = calculate_crc32c(csum32,
+			    (uint8_t *)gd + offset, csum_len);
+		}
 
 		crc = csum32 & 0xFFFF;
 		return (htole16(crc));