Skip to content

Commit eee2218

Browse files
LiBaokun96tytso
authored andcommitted
ext4: add inode table check in __ext4_get_inode_loc to aovid possible infinite loop
In do_writepages, if the value returned by ext4_writepages is "-ENOMEM" and "wbc->sync_mode == WB_SYNC_ALL", retry until the condition is not met. In __ext4_get_inode_loc, if the bh returned by sb_getblk is NULL, the function returns -ENOMEM. In __getblk_slow, if the return value of grow_buffers is less than 0, the function returns NULL. When the three processes are connected in series like the following stack, an infinite loop may occur: do_writepages <--- keep retrying ext4_writepages mpage_map_and_submit_extent mpage_map_one_extent ext4_map_blocks ext4_ext_map_blocks ext4_ext_handle_unwritten_extents ext4_ext_convert_to_initialized ext4_split_extent ext4_split_extent_at __ext4_ext_dirty __ext4_mark_inode_dirty ext4_reserve_inode_write ext4_get_inode_loc __ext4_get_inode_loc <--- return -ENOMEM sb_getblk __getblk_gfp __getblk_slow <--- return NULL grow_buffers grow_dev_page <--- return -ENXIO ret = (block < end_block) ? 1 : -ENXIO; In this issue, bg_inode_table_hi is overwritten as an incorrect value. As a result, `block < end_block` cannot be met in grow_dev_page. Therefore, __ext4_get_inode_loc always returns '-ENOMEM' and do_writepages keeps retrying. As a result, the writeback process is in the D state due to an infinite loop. Add a check on inode table block in the __ext4_get_inode_loc function by referring to ext4_read_inode_bitmap to avoid this infinite loop. Cc: [email protected] Signed-off-by: Baokun Li <[email protected]> Reviewed-by: Ritesh Harjani (IBM) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 6626781 commit eee2218

File tree

1 file changed

+9
-1
lines changed

1 file changed

+9
-1
lines changed

fs/ext4/inode.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -4479,9 +4479,17 @@ static int __ext4_get_inode_loc(struct super_block *sb, unsigned long ino,
44794479
inodes_per_block = EXT4_SB(sb)->s_inodes_per_block;
44804480
inode_offset = ((ino - 1) %
44814481
EXT4_INODES_PER_GROUP(sb));
4482-
block = ext4_inode_table(sb, gdp) + (inode_offset / inodes_per_block);
44834482
iloc->offset = (inode_offset % inodes_per_block) * EXT4_INODE_SIZE(sb);
44844483

4484+
block = ext4_inode_table(sb, gdp);
4485+
if ((block <= le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) ||
4486+
(block >= ext4_blocks_count(EXT4_SB(sb)->s_es))) {
4487+
ext4_error(sb, "Invalid inode table block %llu in "
4488+
"block_group %u", block, iloc->block_group);
4489+
return -EFSCORRUPTED;
4490+
}
4491+
block += (inode_offset / inodes_per_block);
4492+
44854493
bh = sb_getblk(sb, block);
44864494
if (unlikely(!bh))
44874495
return -ENOMEM;

0 commit comments

Comments
 (0)