Skip to content

Commit

Permalink
ext4: Fix use-after-free about sbi->s_mmp_tsk
Browse files Browse the repository at this point in the history
After merge 618f003("ext4: fix memory leak in ext4_fill_super") commit,
we add delay in ext4_remount after "sb->s_flags |= SB_RDONLY", then
remount filesystem with read-only kasan report following warning:
[  888.695326] ==================================================================
[  888.696566] BUG: KASAN: use-after-free in kthread_stop+0x4c/0x2f0
[  888.697599] Write of size 4 at addr ffff8883849e0020 by task mount/2013
[  888.698707]
[  888.698982] CPU: 4 PID: 2013 Comm: mount Not tainted 4.19.95-00013-ga369a6189bbf-dirty torvalds#413
[  888.700376] Hardware name: QEMU Standard PC
[  888.702587] Call Trace:
[  888.703017]  dump_stack+0x108/0x15f
[  888.703615]  print_address_description+0xa5/0x372
[  888.704420]  kasan_report.cold+0x236/0x2a8
[  888.705761]  check_memory_region+0x240/0x270
[  888.706486]  kasan_check_write+0x20/0x30
[  888.707156]  kthread_stop+0x4c/0x2f0
[  888.707776]  ext4_stop_mmpd+0x32/0x90
[  888.708262]  ext4_remount.cold+0xf6/0x116
[  888.712671]  do_remount_sb+0xff/0x460
[  888.714007]  do_mount+0xce3/0x1be0
[  888.717749]  ksys_mount+0xb2/0x150
[  888.718163]  __x64_sys_mount+0x6a/0x80
[  888.718607]  do_syscall_64+0xd9/0x1f0
[  888.719047]  entry_SYSCALL_64_after_hwframe+0x44/0xa9

As kmmpd will exit if filesystem is read-only. Then sbi->s_mmp_tsk become wild
ptr, lead to use-after-free. As kmmpd will exit by others(call ktread_stop)
or by itself. After 618f003 commit we can trigger this issue very easy.
Before this commit also exist this issue.
If kmmpd exit by itself, after merge 618f003 commit there will trigger UAF
when umount filesystem.
To fix this issue, introduce sbi->s_mmp_lock to protect sbi->s_mmp_tsk. If kmmpd
exit by itself, we set sbi->s_mmp_tsk with NULL, and release mmp buffer_head.

Fixes: 618f003 ("ext4: fix memory leak in ext4_fill_super")
Signed-off-by: Ye Bin <yebin10@huawei.com>
  • Loading branch information
Ye Bin authored and intel-lab-lkp committed Jun 29, 2021
1 parent f9505c7 commit 2d31432
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 2 deletions.
1 change: 1 addition & 0 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,7 @@ struct ext4_sb_info {
struct completion s_kobj_unregister;
struct super_block *s_sb;
struct buffer_head *s_mmp_bh;
struct mutex s_mmp_lock;

/* Journaling */
struct journal_s *s_journal;
Expand Down
24 changes: 22 additions & 2 deletions fs/ext4/mmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,9 @@ void __dump_mmp_msg(struct super_block *sb, struct mmp_struct *mmp,
static int kmmpd(void *data)
{
struct super_block *sb = (struct super_block *) data;
struct ext4_super_block *es = EXT4_SB(sb)->s_es;
struct buffer_head *bh = EXT4_SB(sb)->s_mmp_bh;
struct ext4_sb_info *sbi = EXT4_SB(sb);
struct ext4_super_block *es = sbi->s_es;
struct buffer_head *bh = sbi->s_mmp_bh;
struct mmp_struct *mmp;
ext4_fsblk_t mmp_block;
u32 seq = 0;
Expand Down Expand Up @@ -245,16 +246,35 @@ static int kmmpd(void *data)
retval = write_mmp_block(sb, bh);

exit_thread:
/*
* Maybe s_mmp_tsk kthread is stoped by others or by itself. If exit
* by itself then sbi->s_mmp_tsk will be wild ptr, so there is need
* set sbi->s_mmp_tsk with NULL, and also release mmp buffer_head.
*/
while (!kthread_should_stop()) {
if (!mutex_trylock(&sbi->s_mmp_lock))
continue;

if (sbi->s_mmp_tsk) {
sbi->s_mmp_tsk = NULL;
brelse(bh);
}
mutex_unlock(&sbi->s_mmp_lock);
break;
}

return retval;
}

void ext4_stop_mmpd(struct ext4_sb_info *sbi)
{
mutex_lock(&sbi->s_mmp_lock);
if (sbi->s_mmp_tsk) {
kthread_stop(sbi->s_mmp_tsk);
brelse(sbi->s_mmp_bh);
sbi->s_mmp_tsk = NULL;
}
mutex_unlock(&sbi->s_mmp_lock);
}

/*
Expand Down
1 change: 1 addition & 0 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -4789,6 +4789,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
needs_recovery = (es->s_last_orphan != 0 ||
ext4_has_feature_journal_needs_recovery(sb));

mutex_init(&sbi->s_mmp_lock);
if (ext4_has_feature_mmp(sb) && !sb_rdonly(sb))
if (ext4_multi_mount_protect(sb, le64_to_cpu(es->s_mmp_block)))
goto failed_mount3a;
Expand Down

0 comments on commit 2d31432

Please sign in to comment.