Skip to content

Commit afd09b6

Browse files
Alexey Makhalovtytso
authored andcommitted
ext4: fix memory leak in ext4_fill_super
Buffer head references must be released before calling kill_bdev(); otherwise the buffer head (and its page referenced by b_data) will not be freed by kill_bdev, and subsequently that bh will be leaked. If blocksizes differ, sb_set_blocksize() will kill current buffers and page cache by using kill_bdev(). And then super block will be reread again but using correct blocksize this time. sb_set_blocksize() didn't fully free superblock page and buffer head, and being busy, they were not freed and instead leaked. This can easily be reproduced by calling an infinite loop of: systemctl start <ext4_on_lvm>.mount, and systemctl stop <ext4_on_lvm>.mount ... since systemd creates a cgroup for each slice which it mounts, and the bh leak get amplified by a dying memory cgroup that also never gets freed, and memory consumption is much more easily noticed. Fixes: ce40733 ("ext4: Check for return value from sb_set_blocksize") Fixes: ac27a0e ("ext4: initial copy of files from ext3") Link: https://lore.kernel.org/r/20210521075533.95732-1-amakhalov@vmware.com Signed-off-by: Alexey Makhalov <amakhalov@vmware.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@kernel.org
1 parent a7ba36b commit afd09b6

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

fs/ext4/super.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4462,14 +4462,20 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
44624462
}
44634463

44644464
if (sb->s_blocksize != blocksize) {
4465+
/*
4466+
* bh must be released before kill_bdev(), otherwise
4467+
* it won't be freed and its page also. kill_bdev()
4468+
* is called by sb_set_blocksize().
4469+
*/
4470+
brelse(bh);
44654471
/* Validate the filesystem blocksize */
44664472
if (!sb_set_blocksize(sb, blocksize)) {
44674473
ext4_msg(sb, KERN_ERR, "bad block size %d",
44684474
blocksize);
4475+
bh = NULL;
44694476
goto failed_mount;
44704477
}
44714478

4472-
brelse(bh);
44734479
logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
44744480
offset = do_div(logical_sb_block, blocksize);
44754481
bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
@@ -5202,8 +5208,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
52025208
kfree(get_qf_name(sb, sbi, i));
52035209
#endif
52045210
fscrypt_free_dummy_policy(&sbi->s_dummy_enc_policy);
5205-
ext4_blkdev_remove(sbi);
5211+
/* ext4_blkdev_remove() calls kill_bdev(), release bh before it. */
52065212
brelse(bh);
5213+
ext4_blkdev_remove(sbi);
52075214
out_fail:
52085215
sb->s_fs_info = NULL;
52095216
kfree(sbi->s_blockgroup_lock);

0 commit comments

Comments
 (0)