Skip to content

Commit df3da4e

Browse files
Suraj Jitindar Singhtytso
authored andcommitted
ext4: fix potential race between s_group_info online resizing and access
During an online resize an array of pointers to s_group_info gets replaced so it can get enlarged. If there is a concurrent access to the array in ext4_get_group_info() and this memory has been reused then this can lead to an invalid memory access. Link: https://bugzilla.kernel.org/show_bug.cgi?id=206443 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Suraj Jitindar Singh <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Reviewed-by: Balbir Singh <[email protected]> Cc: [email protected]
1 parent 1d0c392 commit df3da4e

File tree

2 files changed

+39
-21
lines changed

2 files changed

+39
-21
lines changed

fs/ext4/ext4.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,7 @@ struct ext4_sb_info {
14621462
#endif
14631463

14641464
/* for buddy allocator */
1465-
struct ext4_group_info ***s_group_info;
1465+
struct ext4_group_info ** __rcu *s_group_info;
14661466
struct inode *s_buddy_cache;
14671467
spinlock_t s_md_lock;
14681468
unsigned short *s_mb_offsets;
@@ -2994,13 +2994,13 @@ static inline
29942994
struct ext4_group_info *ext4_get_group_info(struct super_block *sb,
29952995
ext4_group_t group)
29962996
{
2997-
struct ext4_group_info ***grp_info;
2997+
struct ext4_group_info **grp_info;
29982998
long indexv, indexh;
29992999
BUG_ON(group >= EXT4_SB(sb)->s_groups_count);
3000-
grp_info = EXT4_SB(sb)->s_group_info;
30013000
indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb));
30023001
indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1);
3003-
return grp_info[indexv][indexh];
3002+
grp_info = sbi_array_rcu_deref(EXT4_SB(sb), s_group_info, indexv);
3003+
return grp_info[indexh];
30043004
}
30053005

30063006
/*

fs/ext4/mballoc.c

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,7 +2356,7 @@ int ext4_mb_alloc_groupinfo(struct super_block *sb, ext4_group_t ngroups)
23562356
{
23572357
struct ext4_sb_info *sbi = EXT4_SB(sb);
23582358
unsigned size;
2359-
struct ext4_group_info ***new_groupinfo;
2359+
struct ext4_group_info ***old_groupinfo, ***new_groupinfo;
23602360

23612361
size = (ngroups + EXT4_DESC_PER_BLOCK(sb) - 1) >>
23622362
EXT4_DESC_PER_BLOCK_BITS(sb);
@@ -2369,13 +2369,16 @@ int ext4_mb_alloc_groupinfo(struct super_block *sb, ext4_group_t ngroups)
23692369
ext4_msg(sb, KERN_ERR, "can't allocate buddy meta group");
23702370
return -ENOMEM;
23712371
}
2372-
if (sbi->s_group_info) {
2373-
memcpy(new_groupinfo, sbi->s_group_info,
2372+
rcu_read_lock();
2373+
old_groupinfo = rcu_dereference(sbi->s_group_info);
2374+
if (old_groupinfo)
2375+
memcpy(new_groupinfo, old_groupinfo,
23742376
sbi->s_group_info_size * sizeof(*sbi->s_group_info));
2375-
kvfree(sbi->s_group_info);
2376-
}
2377-
sbi->s_group_info = new_groupinfo;
2377+
rcu_read_unlock();
2378+
rcu_assign_pointer(sbi->s_group_info, new_groupinfo);
23782379
sbi->s_group_info_size = size / sizeof(*sbi->s_group_info);
2380+
if (old_groupinfo)
2381+
ext4_kvfree_array_rcu(old_groupinfo);
23792382
ext4_debug("allocated s_groupinfo array for %d meta_bg's\n",
23802383
sbi->s_group_info_size);
23812384
return 0;
@@ -2387,6 +2390,7 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
23872390
{
23882391
int i;
23892392
int metalen = 0;
2393+
int idx = group >> EXT4_DESC_PER_BLOCK_BITS(sb);
23902394
struct ext4_sb_info *sbi = EXT4_SB(sb);
23912395
struct ext4_group_info **meta_group_info;
23922396
struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits);
@@ -2405,12 +2409,12 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
24052409
"for a buddy group");
24062410
goto exit_meta_group_info;
24072411
}
2408-
sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] =
2409-
meta_group_info;
2412+
rcu_read_lock();
2413+
rcu_dereference(sbi->s_group_info)[idx] = meta_group_info;
2414+
rcu_read_unlock();
24102415
}
24112416

2412-
meta_group_info =
2413-
sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)];
2417+
meta_group_info = sbi_array_rcu_deref(sbi, s_group_info, idx);
24142418
i = group & (EXT4_DESC_PER_BLOCK(sb) - 1);
24152419

24162420
meta_group_info[i] = kmem_cache_zalloc(cachep, GFP_NOFS);
@@ -2458,8 +2462,13 @@ int ext4_mb_add_groupinfo(struct super_block *sb, ext4_group_t group,
24582462
exit_group_info:
24592463
/* If a meta_group_info table has been allocated, release it now */
24602464
if (group % EXT4_DESC_PER_BLOCK(sb) == 0) {
2461-
kfree(sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)]);
2462-
sbi->s_group_info[group >> EXT4_DESC_PER_BLOCK_BITS(sb)] = NULL;
2465+
struct ext4_group_info ***group_info;
2466+
2467+
rcu_read_lock();
2468+
group_info = rcu_dereference(sbi->s_group_info);
2469+
kfree(group_info[idx]);
2470+
group_info[idx] = NULL;
2471+
rcu_read_unlock();
24632472
}
24642473
exit_meta_group_info:
24652474
return -ENOMEM;
@@ -2472,6 +2481,7 @@ static int ext4_mb_init_backend(struct super_block *sb)
24722481
struct ext4_sb_info *sbi = EXT4_SB(sb);
24732482
int err;
24742483
struct ext4_group_desc *desc;
2484+
struct ext4_group_info ***group_info;
24752485
struct kmem_cache *cachep;
24762486

24772487
err = ext4_mb_alloc_groupinfo(sb, ngroups);
@@ -2507,11 +2517,16 @@ static int ext4_mb_init_backend(struct super_block *sb)
25072517
while (i-- > 0)
25082518
kmem_cache_free(cachep, ext4_get_group_info(sb, i));
25092519
i = sbi->s_group_info_size;
2520+
rcu_read_lock();
2521+
group_info = rcu_dereference(sbi->s_group_info);
25102522
while (i-- > 0)
2511-
kfree(sbi->s_group_info[i]);
2523+
kfree(group_info[i]);
2524+
rcu_read_unlock();
25122525
iput(sbi->s_buddy_cache);
25132526
err_freesgi:
2514-
kvfree(sbi->s_group_info);
2527+
rcu_read_lock();
2528+
kvfree(rcu_dereference(sbi->s_group_info));
2529+
rcu_read_unlock();
25152530
return -ENOMEM;
25162531
}
25172532

@@ -2700,7 +2715,7 @@ int ext4_mb_release(struct super_block *sb)
27002715
ext4_group_t ngroups = ext4_get_groups_count(sb);
27012716
ext4_group_t i;
27022717
int num_meta_group_infos;
2703-
struct ext4_group_info *grinfo;
2718+
struct ext4_group_info *grinfo, ***group_info;
27042719
struct ext4_sb_info *sbi = EXT4_SB(sb);
27052720
struct kmem_cache *cachep = get_groupinfo_cache(sb->s_blocksize_bits);
27062721

@@ -2719,9 +2734,12 @@ int ext4_mb_release(struct super_block *sb)
27192734
num_meta_group_infos = (ngroups +
27202735
EXT4_DESC_PER_BLOCK(sb) - 1) >>
27212736
EXT4_DESC_PER_BLOCK_BITS(sb);
2737+
rcu_read_lock();
2738+
group_info = rcu_dereference(sbi->s_group_info);
27222739
for (i = 0; i < num_meta_group_infos; i++)
2723-
kfree(sbi->s_group_info[i]);
2724-
kvfree(sbi->s_group_info);
2740+
kfree(group_info[i]);
2741+
kvfree(group_info);
2742+
rcu_read_unlock();
27252743
}
27262744
kfree(sbi->s_mb_offsets);
27272745
kfree(sbi->s_mb_maxs);

0 commit comments

Comments
 (0)