Re: [PATCH 2/3] fix bitmap byte size calculations

Andreas Dilger <[email protected]> Fri, 31 Jul 2026 02:57:48 -0600
Newsgroups org.kernel.vger.linux-ext4
Message-ID <[email protected]>
On Jul 30, 2026, at 21:37, Josh Hunt <[email protected]> wrote:
>=20
> From: Kit Knox <[email protected]>
>=20
> The bitmap byte size calculations use integer division (count / 8) =
without
> rounding up.  When clusters_per_group or inodes_per_group is not a =
multiple
> of 8, this allocates too few bytes.  The bitmap backends copy (num + =
7) / 8
> bytes via ext2fs_get_block_bitmap_range2(), causing a buffer overrun =
of 1-7
> bytes.

Is it ever legal to have clusters_per_group or inodes_per_group in a =
filesystem?
Is this something you've seen in real life or is this a fuzzed =
filesystem image?
It would probably be better to refuse processing such a filesystem =
entirely, since
it is more likely that the superblock is corrupt and a backup superblock =
should be
used instead?

Testing mke2fs it doesn't appear to allow a non-multiple-of-8 value for =
blocks_per_group:

    $ mke2fs -N 77 -g 77 /tmp/tt
    mke2fs: blocks per group must be multiple of 8

and while the inode count is specified as an odd number, it is =
internally rounded
to a multiple of 8 per group:

    $ mke2fs -N 77 -g 800 /tmp/tt
    mke2fs 1.47.3-wc2 (11-Nov-2025)
    :
    Creating filesystem with 262144 4k blocks and 5248 inodes
    Superblock backups stored on blocks:  2400, 194400
    :
    $ dumpe2fs -h /tmp/tt | grep -i group
    dumpe2fs 1.47.3-wc2 (11-Nov-2025)
    Blocks per group:         800
    Fragments per group:      800
    Inodes per group:         16
    Inode blocks per group:   1

even with 4KiB inode size (one inode per block) the number of inodes is
a multiple of 8:

    $ mke2fs -N 77 -I 4096 /tmp/tt
    mke2fs 1.47.3-wc2 (11-Nov-2025)
    :
    Creating filesystem with 262144 4k blocks and 64 inodes
    :
    $ dumpe2fs -h /tmp/tt | grep -i group
    dumpe2fs 1.47.3-wc2 (11-Nov-2025)
    Blocks per group:         32768
    Fragments per group:      32768
    Inodes per group:         8
    Inode blocks per group:   8

Cheers, Andreas

>=20
> Example: With s_clusters_per_group =3D blocksize*8 + 1, the buffer is
> undersized by 1 byte, causing heap corruption when the bitmap is =
copied.
>=20
> Signed-off-by: Kit Knox <[email protected]>
> ---
> e2fsck/pass5.c          | 10 ++++++----
> lib/ext2fs/rw_bitmaps.c | 13 ++++++-------
> misc/dumpe2fs.c         |  4 ++--
> resize/resize2fs.c      |  3 ++-
> 4 files changed, 16 insertions(+), 14 deletions(-)
>=20
> diff --git a/e2fsck/pass5.c b/e2fsck/pass5.c
> index c1d45a5f..aad2e76a 100644
> --- a/e2fsck/pass5.c
> +++ b/e2fsck/pass5.c
> @@ -324,10 +324,12 @@ static void check_block_bitmaps(e2fsck_t ctx)
> int redo_flag =3D 0;
> char *actual_buf, *bitmap_buf;
>=20
> - actual_buf =3D (char *) e2fsck_allocate_memory(ctx, fs->blocksize,
> -     "actual bitmap buffer");
> - bitmap_buf =3D (char *) e2fsck_allocate_memory(ctx, fs->blocksize,
> -     "bitmap block buffer");
> + actual_buf =3D (char *) e2fsck_allocate_memory(ctx,
> + ext2fs_div_ceil(fs->super->s_clusters_per_group, 8),
> + "actual bitmap buffer");
> + bitmap_buf =3D (char *) e2fsck_allocate_memory(ctx,
> + ext2fs_div_ceil(fs->super->s_clusters_per_group, 8),
> + "bitmap block buffer");
>=20
> clear_problem_context(&pctx);
> free_array =3D (unsigned int *) e2fsck_allocate_memory(ctx,
> diff --git a/lib/ext2fs/rw_bitmaps.c b/lib/ext2fs/rw_bitmaps.c
> index 1da75e4a..211d9ec4 100644
> --- a/lib/ext2fs/rw_bitmaps.c
> +++ b/lib/ext2fs/rw_bitmaps.c
> @@ -72,15 +72,14 @@ static errcode_t write_bitmaps(ext2_filsys fs, int =
do_inode, int do_block)
>=20
> inode_nbytes =3D block_nbytes =3D 0;
> if (do_block) {
> - block_nbytes =3D EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
> + block_nbytes =3D ext2fs_div_ceil(EXT2_CLUSTERS_PER_GROUP(fs->super), =
8);
> retval =3D io_channel_alloc_buf(fs->io, 0, &block_buf);
> if (retval)
> goto errout;
> memset(block_buf, 0xff, fs->blocksize);
> }
> if (do_inode) {
> - inode_nbytes =3D (size_t)
> - ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
> + inode_nbytes =3D ext2fs_div_ceil(EXT2_INODES_PER_GROUP(fs->super), =
8);
> retval =3D io_channel_alloc_buf(fs->io, 0, &inode_buf);
> if (retval)
> goto errout;
> @@ -230,8 +229,8 @@ static int bitmap_tail_verify(unsigned char =
*bitmap, int first, int last)
> static errcode_t read_bitmaps_range_prepare(ext2_filsys fs, int flags)
> {
> errcode_t retval;
> - int block_nbytes =3D EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
> - int inode_nbytes =3D EXT2_INODES_PER_GROUP(fs->super) / 8;
> + int block_nbytes =3D =
ext2fs_div_ceil(EXT2_CLUSTERS_PER_GROUP(fs->super), 8);
> + int inode_nbytes =3D =
ext2fs_div_ceil(EXT2_INODES_PER_GROUP(fs->super), 8);
> char *buf;
>=20
> EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
> @@ -289,8 +288,8 @@ static errcode_t =
read_bitmaps_range_start(ext2_filsys fs, int flags,
> dgrp_t i;
> char *block_bitmap =3D 0, *inode_bitmap =3D 0;
> errcode_t retval =3D 0;
> - int block_nbytes =3D EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
> - int inode_nbytes =3D EXT2_INODES_PER_GROUP(fs->super) / 8;
> + int block_nbytes =3D =
ext2fs_div_ceil(EXT2_CLUSTERS_PER_GROUP(fs->super), 8);
> + int inode_nbytes =3D =
ext2fs_div_ceil(EXT2_INODES_PER_GROUP(fs->super), 8);
> int csum_flag;
> unsigned int cnt;
> blk64_t blk;
> diff --git a/misc/dumpe2fs.c b/misc/dumpe2fs.c
> index b56d15d9..2c45d6bb 100644
> --- a/misc/dumpe2fs.c
> +++ b/misc/dumpe2fs.c
> @@ -168,8 +168,8 @@ static void list_desc(ext2_filsys fs, int =
grp_only)
> if (ext2fs_has_feature_bigalloc(fs->super))
> units =3D _("clusters");
>=20
> - block_nbytes =3D EXT2_CLUSTERS_PER_GROUP(fs->super) / 8;
> - inode_nbytes =3D EXT2_INODES_PER_GROUP(fs->super) / 8;
> + block_nbytes =3D ext2fs_div_ceil(EXT2_CLUSTERS_PER_GROUP(fs->super), =
8);
> + inode_nbytes =3D ext2fs_div_ceil(EXT2_INODES_PER_GROUP(fs->super), =
8);
>=20
> if (fs->block_map)
> block_bitmap =3D malloc(block_nbytes);
> diff --git a/resize/resize2fs.c b/resize/resize2fs.c
> index dfefca1d..ad092244 100644
> --- a/resize/resize2fs.c
> +++ b/resize/resize2fs.c
> @@ -2088,6 +2088,7 @@ static int fix_ea_ibody_entries(ext2_extent =
imap,
>=20
> ea_magic =3D (__u32 *)((char *)inode + EXT2_GOOD_OLD_INODE_SIZE +
> inode->i_extra_isize);
> +
> if (*ea_magic !=3D EXT2_EXT_ATTR_MAGIC)
> return 0;
>=20
> @@ -2950,7 +2951,7 @@ static errcode_t =
resize2fs_calculate_summary_stats(ext2_filsys fs)
> /*
> * First calculate the block statistics
> */
> - bitmap_buf =3D malloc(fs->blocksize);
> + bitmap_buf =3D =
malloc(ext2fs_div_ceil(fs->super->s_clusters_per_group, 8));
> if (!bitmap_buf)
> return ENOMEM;
> for (group =3D 0; group < fs->group_desc_count; group++) {
> --=20
> 2.34.1
>=20
>=20


Cheers, Andreas