alloc_icount() computes the EXT2_ICOUNT_OPT_FULLMAP allocation size in an "unsigned int": unsigned sz = sizeof(*icount->fullmap) * icount->num_inodes; num_inodes is a 32-bit ext2_ino_t taken from s_inodes_count, so for file systems with s_inodes_count >= 2^31 the assignment truncates the product: at s_inodes_count == 2^31 the allocation collapses to zero bytes, and at the ext4 maximum of 2^32 - 1 (seen in the field on an 84 TiB file system created with a dense inode ratio) only half of the array is allocated. Running e2fsck -E inode_count_fullmap on such a file system then reads and writes gigabytes past the end of the buffer in pass 2 and pass 4, corrupting e2fsck's own heap while it decides what to repair. The array is also one element too small on every file system: valid inode numbers run from 1 to num_inodes inclusive and fullmap is indexed by inode number, but only num_inodes elements are allocated, so fullmap[num_inodes] is out of bounds. Pass 4 fetches the count of every inode up to and including s_inodes_count, so every fullmap run performs a 2-byte out-of-bounds access of the last element. Compute the size in size_t, allocate num_inodes + 1 entries, and skip the fullmap optimization (falling back to the bitmap + list variant) when the size would not fit in the unsigned long taken by ext2fs_get_mem(), which also keeps the computation from wrapping on 32-bit builds. Signed-off-by: Ting-Chang Hou --- lib/ext2fs/icount.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/ext2fs/icount.c b/lib/ext2fs/icount.c index 888a90b2..08b6bb28 100644 --- a/lib/ext2fs/icount.c +++ b/lib/ext2fs/icount.c @@ -115,8 +115,19 @@ static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret) icount->num_inodes = fs->super->s_inodes_count; if ((flags & EXT2_ICOUNT_OPT_FULLMAP) && - (flags & EXT2_ICOUNT_OPT_INCREMENT)) { - unsigned sz = sizeof(*icount->fullmap) * icount->num_inodes; + (flags & EXT2_ICOUNT_OPT_INCREMENT) && + icount->num_inodes < ~0UL / sizeof(*icount->fullmap)) { + /* + * fullmap is indexed by inode number, which runs from + * 1 to num_inodes inclusive; allocate num_inodes + 1 + * entries. num_inodes can be as large as 2^32 - 1, so + * compute the size in size_t and skip fullmap (falling + * back to the bitmap + list variant below) where the + * result would not fit in the unsigned long taken by + * ext2fs_get_mem(). + */ + size_t sz = sizeof(*icount->fullmap) * + ((size_t) icount->num_inodes + 1); retval = ext2fs_get_mem(sz, &icount->fullmap); /* If we can't allocate, fall back */ -- 2.34.1 Disclaimer: The contents of this e-mail message and any attachments are confidential and are intended solely for addressee. The information may also be legally privileged. This transmission is sent in trust, for the sole purpose of delivery to the intended recipient. If you have received this transmission in error, any use, reproduction or dissemination of this transmission is strictly prohibited. If you are not the intended recipient, please immediately notify the sender by reply e-mail or phone and delete this message and its attachments, if any.