The commit is pushed to "branch-rh9-5.14.vz9.1.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git after ark-5.14 ------> commit b90d6aec7e49f5fef4074b121ed67297781ff885 Author: Oleg Babin <oba...@virtuozzo.com> Date: Thu Sep 30 16:03:55 2021 +0300
ext4: replace ext4_kvmalloc() with kvmalloc() ext4_kvmalloc() is used to allocate the table of group descritors blocks. It is called in GFP_NOFS context which is not vmalloc() compatible so it cannot be directly replaced with kvmalloc(). In order to use kvmalloc() with GFP_KERNEL flag the memory allocation is moved from add_new_gdb() / add_new_gdb_meta_bg() functions to the beginning of the resize process before any journaling is started and any FS locks are taken. After this we do not need ext4_kvmalloc/ext4_kvfree functions any more. https://jira.sw.ru/browse/PSBM-83044 Signed-off-by: Oleg Babin <oba...@virtuozzo.com> Signed-off-by: Jan Dakinevich <jan.dakinev...@virtuozzo.com> +++ ext4: fix out of bounds access in ext4_alloc_group_desc_bh_array() https://jira.sw.ru/browse/PSBM-87413 mFixes: d695abe ("ext4: replace ext4_kvmalloc() with kvmalloc()") Signed-off-by: Jan Dakinevich <jan.dakinev...@virtuozzo.com> (cherry-picked from vz7 commit cfd1ff8794a4 ("ext4: replace ext4_kvmalloc() with kvmalloc()")) https://jira.sw.ru/browse/PSBM-127849 Signed-off-by: Valeriy Vdovin <valeriy.vdo...@virtuozzo.com> +++ ext4: Fix high probable use-after-free Here we have even worse race than in mainstream. https://jira.sw.ru/browse/PSBM-101798 mFixes: 86521524314e "ext4: replace ext4_kvmalloc() with kvmalloc()" Signed-off-by: Kirill Tkhai <ktk...@virtuozzo.com> (cherry picked from vz7 commit 601cc650f4ef ("ext4: Fix high probable use-after-free")) mFixes: vz8 commit 5e0235ca2ae8 ("ext4: replace ext4_kvmalloc() with kvmalloc()") In the scope of https://jira.sw.ru/browse/PSBM-127850 Signed-off-by: Konstantin Khorenko <khore...@virtuozzo.com> (cherry picked from vz8 commit 781a1d7846ae4c0b1e9cb3e3ad0b8897242027fb) Signed-off-by: Andrey Zhadchenko <andrey.zhadche...@virtuozzo.com> --- fs/ext4/ext4.h | 2 ++ fs/ext4/resize.c | 49 ++++++++++--------------------------------------- fs/ext4/super.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 39 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3c51e243450d..ca89e6880e24 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -3050,6 +3050,8 @@ extern int ext4_calculate_overhead(struct super_block *sb); extern void ext4_superblock_csum_set(struct super_block *sb); extern int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup); +extern int ext4_alloc_group_desc_bh_array(struct super_block *sb, + ext4_group_t ngroup); extern const char *ext4_decode_error(struct super_block *sb, int errno, char nbuf[16]); extern void ext4_mark_group_bitmap_corrupted(struct super_block *sb, diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 7a9f1adef679..d9d97ee7dcbc 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -797,7 +797,6 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, struct ext4_super_block *es = EXT4_SB(sb)->s_es; unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb); ext4_fsblk_t gdblock = EXT4_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num; - struct buffer_head **o_group_desc, **n_group_desc = NULL; struct buffer_head *dind = NULL; struct buffer_head *gdb_bh = NULL; int gdbackups; @@ -858,15 +857,6 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, if (unlikely(err)) goto errout; - n_group_desc = kvmalloc((gdb_num + 1) * sizeof(struct buffer_head *), - GFP_KERNEL); - if (!n_group_desc) { - err = -ENOMEM; - ext4_warning(sb, "not enough memory for %lu groups", - gdb_num + 1); - goto errout; - } - /* * Finally, we have all of the possible failures behind us... * @@ -894,15 +884,8 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, } brelse(dind); - rcu_read_lock(); - o_group_desc = rcu_dereference(EXT4_SB(sb)->s_group_desc); - memcpy(n_group_desc, o_group_desc, - EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); - rcu_read_unlock(); - n_group_desc[gdb_num] = gdb_bh; - rcu_assign_pointer(EXT4_SB(sb)->s_group_desc, n_group_desc); + rcu_assign_pointer(EXT4_SB(sb)->s_group_desc[gdb_num], gdb_bh); EXT4_SB(sb)->s_gdb_count++; - ext4_kvfree_array_rcu(o_group_desc); lock_buffer(EXT4_SB(sb)->s_sbh); le16_add_cpu(&es->s_reserved_gdt_blocks, -1); @@ -913,7 +896,6 @@ static int add_new_gdb(handle_t *handle, struct inode *inode, ext4_std_error(sb, err); return err; errout: - kvfree(n_group_desc); brelse(iloc.bh); brelse(dind); brelse(gdb_bh); @@ -929,7 +911,6 @@ static int add_new_gdb_meta_bg(struct super_block *sb, handle_t *handle, ext4_group_t group) { ext4_fsblk_t gdblock; struct buffer_head *gdb_bh; - struct buffer_head **o_group_desc, **n_group_desc; unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb); int err; @@ -938,34 +919,16 @@ static int add_new_gdb_meta_bg(struct super_block *sb, gdb_bh = ext4_sb_bread(sb, gdblock, 0); if (IS_ERR(gdb_bh)) return PTR_ERR(gdb_bh); - n_group_desc = kvmalloc((gdb_num + 1) * sizeof(struct buffer_head *), - GFP_KERNEL); - if (!n_group_desc) { - brelse(gdb_bh); - err = -ENOMEM; - ext4_warning(sb, "not enough memory for %lu groups", - gdb_num + 1); - return err; - } - - rcu_read_lock(); - o_group_desc = rcu_dereference(EXT4_SB(sb)->s_group_desc); - memcpy(n_group_desc, o_group_desc, - EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); - rcu_read_unlock(); - n_group_desc[gdb_num] = gdb_bh; BUFFER_TRACE(gdb_bh, "get_write_access"); err = ext4_journal_get_write_access(handle, gdb_bh); if (err) { - kvfree(n_group_desc); brelse(gdb_bh); return err; } - rcu_assign_pointer(EXT4_SB(sb)->s_group_desc, n_group_desc); + rcu_assign_pointer(EXT4_SB(sb)->s_group_desc[gdb_num], gdb_bh); EXT4_SB(sb)->s_gdb_count++; - ext4_kvfree_array_rcu(o_group_desc); return err; } @@ -1688,6 +1651,10 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) if (err) goto out; + err = ext4_alloc_group_desc_bh_array(sb, input->group + 1); + if (err) + goto out; + err = ext4_mb_alloc_groupinfo(sb, input->group + 1); if (err) goto out; @@ -2066,6 +2033,10 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) if (err) goto out; + err = ext4_alloc_group_desc_bh_array(sb, n_group + 1); + if (err) + goto out; + err = ext4_mb_alloc_groupinfo(sb, n_group + 1); if (err) goto out; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index befbb0892fdd..0186d0421c2b 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2752,6 +2752,37 @@ int ext4_alloc_flex_bg_array(struct super_block *sb, ext4_group_t ngroup) return 0; } +/* + * Allocate the top-level s_group_desc array for the specified number + * of groups. As the memory is allocated before the journaling is started + * we can safely use kvmalloc() with GFP_KERNEL flag here. + */ +int ext4_alloc_group_desc_bh_array(struct super_block *sb, ext4_group_t ngroup) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + unsigned long num_desc = DIV_ROUND_UP(ngroup, EXT4_DESC_PER_BLOCK(sb)); + struct buffer_head **o_group_desc, **n_group_desc; + + if (num_desc <= sbi->s_gdb_count) + return 0; + + n_group_desc = kvmalloc(num_desc * sizeof(struct buffer_head *), + GFP_KERNEL); + if (!n_group_desc) { + ext4_warning(sb, "not enough memory for %lu groups", num_desc); + return -ENOMEM; + } + + o_group_desc = sbi->s_group_desc; + memcpy(n_group_desc, o_group_desc, + sbi->s_gdb_count * sizeof(struct buffer_head *)); + WRITE_ONCE(sbi->s_group_desc, n_group_desc); + + /* FIXME: rcu is needed here. See ms commit 1d0c3924a92e */ + kvfree(o_group_desc); + return 0; +} + static int ext4_fill_flex_info(struct super_block *sb) { struct ext4_sb_info *sbi = EXT4_SB(sb); _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel