[Devel] [PATCH RHEL7 COMMIT] ext4: replace ext4_kvmalloc() with kvmalloc()
Konstantin Khorenko
khorenko at virtuozzo.com
Thu Dec 26 14:40:58 MSK 2019
The commit is pushed to "branch-rh7-3.10.0-1062.7.1.vz7.130.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-1062.7.1.vz7.130.5
------>
commit cfd1ff8794a40176e13aaedd04df2c17e6969143
Author: Oleg Babin <obabin at virtuozzo.com>
Date: Thu Jul 26 18:14:51 2018 +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 <obabin at virtuozzo.com>
Signed-off-by: Jan Dakinevich <jan.dakinevich at 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.dakinevich at virtuozzo.com>
---
fs/ext4/ext4.h | 4 ++--
fs/ext4/resize.c | 44 ++++++++++----------------------------------
fs/ext4/super.c | 29 +++++++++++++++++++++++++++++
3 files changed, 41 insertions(+), 36 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index a687fe140fbe7..db93aa5b6a9c6 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2289,10 +2289,10 @@ extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count);
/* super.c */
extern int ext4_calculate_overhead(struct super_block *sb);
extern void ext4_superblock_csum_set(struct super_block *sb);
-extern void *ext4_kvmalloc(size_t size, gfp_t flags);
-extern void *ext4_kvzalloc(size_t size, gfp_t flags);
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]);
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 353615a36217d..60aef84eb8542 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -756,7 +756,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;
struct buffer_head *dind;
struct buffer_head *gdb_bh;
int gdbackups;
@@ -814,16 +813,6 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
if (unlikely(err))
goto exit_dind;
- n_group_desc = ext4_kvmalloc((gdb_num + 1) *
- sizeof(struct buffer_head *),
- GFP_NOFS);
- if (!n_group_desc) {
- err = -ENOMEM;
- ext4_warning(sb, "not enough memory for %lu groups",
- gdb_num + 1);
- goto exit_inode;
- }
-
/*
* Finally, we have all of the possible failures behind us...
*
@@ -850,13 +839,8 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
}
brelse(dind);
- o_group_desc = EXT4_SB(sb)->s_group_desc;
- memcpy(n_group_desc, o_group_desc,
- EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *));
- n_group_desc[gdb_num] = gdb_bh;
- EXT4_SB(sb)->s_group_desc = n_group_desc;
+ EXT4_SB(sb)->s_group_desc[gdb_num] = gdb_bh;
EXT4_SB(sb)->s_gdb_count++;
- kvfree(o_group_desc);
le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
err = ext4_handle_dirty_super(handle, sb);
@@ -866,7 +850,6 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
return err;
exit_inode:
- kvfree(n_group_desc);
brelse(iloc.bh);
exit_dind:
brelse(dind);
@@ -884,7 +867,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;
@@ -893,23 +875,9 @@ static int add_new_gdb_meta_bg(struct super_block *sb,
gdb_bh = sb_bread(sb, gdblock);
if (!gdb_bh)
return -EIO;
- n_group_desc = ext4_kvmalloc((gdb_num + 1) *
- sizeof(struct buffer_head *),
- GFP_NOFS);
- if (!n_group_desc) {
- err = -ENOMEM;
- ext4_warning(sb, "not enough memory for %lu groups",
- gdb_num + 1);
- return err;
- }
- o_group_desc = EXT4_SB(sb)->s_group_desc;
- memcpy(n_group_desc, o_group_desc,
- EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *));
- n_group_desc[gdb_num] = gdb_bh;
- EXT4_SB(sb)->s_group_desc = n_group_desc;
+ EXT4_SB(sb)->s_group_desc[gdb_num] = gdb_bh;
EXT4_SB(sb)->s_gdb_count++;
- kvfree(o_group_desc);
BUFFER_TRACE(gdb_bh, "get_write_access");
err = ext4_journal_get_write_access(handle, gdb_bh);
if (unlikely(err))
@@ -1629,6 +1597,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;
@@ -2001,6 +1973,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 683d932300f9c..f4af9e8533022 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2207,6 +2207,35 @@ 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 **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;
+ }
+
+ memcpy(n_group_desc, sbi->s_group_desc,
+ sbi->s_gdb_count * sizeof(struct buffer_head *));
+ kvfree(sbi->s_group_desc);
+
+ sbi->s_group_desc = n_group_desc;
+ return 0;
+}
+
static int ext4_fill_flex_info(struct super_block *sb)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
More information about the Devel
mailing list