DF-0876 / fix.diff
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)); |