| From cdce59a1549190b66f8e3fe465c2b2f714b98a94 Mon Sep 17 00:00:00 2001 |
| From: Ritesh Harjani <riteshh@linux.ibm.com> |
| Date: Mon, 17 Jan 2022 17:41:49 +0530 |
| Subject: ext4: fix error handling in ext4_fc_record_modified_inode() |
| |
| From: Ritesh Harjani <riteshh@linux.ibm.com> |
| |
| commit cdce59a1549190b66f8e3fe465c2b2f714b98a94 upstream. |
| |
| Current code does not fully takes care of krealloc() error case, which |
| could lead to silent memory corruption or a kernel bug. This patch |
| fixes that. |
| |
| Also it cleans up some duplicated error handling logic from various |
| functions in fast_commit.c file. |
| |
| Reported-by: luo penghao <luo.penghao@zte.com.cn> |
| Suggested-by: Lukas Czerner <lczerner@redhat.com> |
| Signed-off-by: Ritesh Harjani <riteshh@linux.ibm.com> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| Link: https://lore.kernel.org/r/62e8b6a1cce9359682051deb736a3c0953c9d1e9.1642416995.git.riteshh@linux.ibm.com |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Cc: stable@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/ext4/fast_commit.c | 64 ++++++++++++++++++++++---------------------------- |
| 1 file changed, 29 insertions(+), 35 deletions(-) |
| |
| --- a/fs/ext4/fast_commit.c |
| +++ b/fs/ext4/fast_commit.c |
| @@ -1388,14 +1388,15 @@ static int ext4_fc_record_modified_inode |
| if (state->fc_modified_inodes[i] == ino) |
| return 0; |
| if (state->fc_modified_inodes_used == state->fc_modified_inodes_size) { |
| - state->fc_modified_inodes_size += |
| - EXT4_FC_REPLAY_REALLOC_INCREMENT; |
| state->fc_modified_inodes = krealloc( |
| - state->fc_modified_inodes, sizeof(int) * |
| - state->fc_modified_inodes_size, |
| - GFP_KERNEL); |
| + state->fc_modified_inodes, |
| + sizeof(int) * (state->fc_modified_inodes_size + |
| + EXT4_FC_REPLAY_REALLOC_INCREMENT), |
| + GFP_KERNEL); |
| if (!state->fc_modified_inodes) |
| return -ENOMEM; |
| + state->fc_modified_inodes_size += |
| + EXT4_FC_REPLAY_REALLOC_INCREMENT; |
| } |
| state->fc_modified_inodes[state->fc_modified_inodes_used++] = ino; |
| return 0; |
| @@ -1427,7 +1428,9 @@ static int ext4_fc_replay_inode(struct s |
| } |
| inode = NULL; |
| |
| - ext4_fc_record_modified_inode(sb, ino); |
| + ret = ext4_fc_record_modified_inode(sb, ino); |
| + if (ret) |
| + goto out; |
| |
| raw_fc_inode = (struct ext4_inode *) |
| (val + offsetof(struct ext4_fc_inode, fc_raw_inode)); |
| @@ -1626,6 +1629,8 @@ static int ext4_fc_replay_add_range(stru |
| } |
| |
| ret = ext4_fc_record_modified_inode(sb, inode->i_ino); |
| + if (ret) |
| + goto out; |
| |
| start = le32_to_cpu(ex->ee_block); |
| start_pblk = ext4_ext_pblock(ex); |
| @@ -1643,18 +1648,14 @@ static int ext4_fc_replay_add_range(stru |
| map.m_pblk = 0; |
| ret = ext4_map_blocks(NULL, inode, &map, 0); |
| |
| - if (ret < 0) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (ret < 0) |
| + goto out; |
| |
| if (ret == 0) { |
| /* Range is not mapped */ |
| path = ext4_find_extent(inode, cur, NULL, 0); |
| - if (IS_ERR(path)) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (IS_ERR(path)) |
| + goto out; |
| memset(&newex, 0, sizeof(newex)); |
| newex.ee_block = cpu_to_le32(cur); |
| ext4_ext_store_pblock( |
| @@ -1668,10 +1669,8 @@ static int ext4_fc_replay_add_range(stru |
| up_write((&EXT4_I(inode)->i_data_sem)); |
| ext4_ext_drop_refs(path); |
| kfree(path); |
| - if (ret) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (ret) |
| + goto out; |
| goto next; |
| } |
| |
| @@ -1684,10 +1683,8 @@ static int ext4_fc_replay_add_range(stru |
| ret = ext4_ext_replay_update_ex(inode, cur, map.m_len, |
| ext4_ext_is_unwritten(ex), |
| start_pblk + cur - start); |
| - if (ret) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (ret) |
| + goto out; |
| /* |
| * Mark the old blocks as free since they aren't used |
| * anymore. We maintain an array of all the modified |
| @@ -1707,10 +1704,8 @@ static int ext4_fc_replay_add_range(stru |
| ext4_ext_is_unwritten(ex), map.m_pblk); |
| ret = ext4_ext_replay_update_ex(inode, cur, map.m_len, |
| ext4_ext_is_unwritten(ex), map.m_pblk); |
| - if (ret) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (ret) |
| + goto out; |
| /* |
| * We may have split the extent tree while toggling the state. |
| * Try to shrink the extent tree now. |
| @@ -1722,6 +1717,7 @@ next: |
| } |
| ext4_ext_replay_shrink_inode(inode, i_size_read(inode) >> |
| sb->s_blocksize_bits); |
| +out: |
| iput(inode); |
| return 0; |
| } |
| @@ -1751,6 +1747,8 @@ ext4_fc_replay_del_range(struct super_bl |
| } |
| |
| ret = ext4_fc_record_modified_inode(sb, inode->i_ino); |
| + if (ret) |
| + goto out; |
| |
| jbd_debug(1, "DEL_RANGE, inode %ld, lblk %d, len %d\n", |
| inode->i_ino, le32_to_cpu(lrange.fc_lblk), |
| @@ -1760,10 +1758,8 @@ ext4_fc_replay_del_range(struct super_bl |
| map.m_len = remaining; |
| |
| ret = ext4_map_blocks(NULL, inode, &map, 0); |
| - if (ret < 0) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (ret < 0) |
| + goto out; |
| if (ret > 0) { |
| remaining -= ret; |
| cur += ret; |
| @@ -1778,15 +1774,13 @@ ext4_fc_replay_del_range(struct super_bl |
| ret = ext4_ext_remove_space(inode, lrange.fc_lblk, |
| lrange.fc_lblk + lrange.fc_len - 1); |
| up_write(&EXT4_I(inode)->i_data_sem); |
| - if (ret) { |
| - iput(inode); |
| - return 0; |
| - } |
| + if (ret) |
| + goto out; |
| ext4_ext_replay_shrink_inode(inode, |
| i_size_read(inode) >> sb->s_blocksize_bits); |
| ext4_mark_inode_dirty(NULL, inode); |
| +out: |
| iput(inode); |
| - |
| return 0; |
| } |
| |