Skip to content

Commit d15e4b0

Browse files
mrpretytso
authored andcommitted
ext4: fix use-after-free in update_super_work when racing with umount
Commit b98535d ("ext4: fix bug_on in start_this_handle during umount filesystem") moved ext4_unregister_sysfs() before flushing s_sb_upd_work to prevent new error work from being queued via /proc/fs/ext4/xx/mb_groups reads during unmount. However, this introduced a use-after-free because update_super_work calls ext4_notify_error_sysfs() -> sysfs_notify() which accesses the kobject's kernfs_node after it has been freed by kobject_del() in ext4_unregister_sysfs(): update_super_work ext4_put_super ----------------- -------------- ext4_unregister_sysfs(sb) kobject_del(&sbi->s_kobj) __kobject_del() sysfs_remove_dir() kobj->sd = NULL sysfs_put(sd) kernfs_put() // RCU free ext4_notify_error_sysfs(sbi) sysfs_notify(&sbi->s_kobj) kn = kobj->sd // stale pointer kernfs_get(kn) // UAF on freed kernfs_node ext4_journal_destroy() flush_work(&sbi->s_sb_upd_work) Instead of reordering the teardown sequence, fix this by making ext4_notify_error_sysfs() detect that sysfs has already been torn down by checking s_kobj.state_in_sysfs, and skipping the sysfs_notify() call in that case. A dedicated mutex (s_error_notify_mutex) serializes ext4_notify_error_sysfs() against kobject_del() in ext4_unregister_sysfs() to prevent TOCTOU races where the kobject could be deleted between the state_in_sysfs check and the sysfs_notify() call. Fixes: b98535d ("ext4: fix bug_on in start_this_handle during umount filesystem") Cc: Jiayuan Chen <jiayuan.chen@linux.dev> Suggested-by: Jan Kara <jack@suse.cz> Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com> Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260319120336.157873-1-jiayuan.chen@linux.dev Signed-off-by: Theodore Ts'o <tytso@mit.edu> Cc: stable@kernel.org
1 parent 496bb99 commit d15e4b0

3 files changed

Lines changed: 11 additions & 1 deletion

File tree

fs/ext4/ext4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,7 @@ struct ext4_sb_info {
15701570
struct proc_dir_entry *s_proc;
15711571
struct kobject s_kobj;
15721572
struct completion s_kobj_unregister;
1573+
struct mutex s_error_notify_mutex; /* protects sysfs_notify vs kobject_del */
15731574
struct super_block *s_sb;
15741575
struct buffer_head *s_mmp_bh;
15751576

fs/ext4/super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5406,6 +5406,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
54065406

54075407
timer_setup(&sbi->s_err_report, print_daily_error_info, 0);
54085408
spin_lock_init(&sbi->s_error_lock);
5409+
mutex_init(&sbi->s_error_notify_mutex);
54095410
INIT_WORK(&sbi->s_sb_upd_work, update_super_work);
54105411

54115412
err = ext4_group_desc_init(sb, es, logical_sb_block, &first_not_zeroed);

fs/ext4/sysfs.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,10 @@ static const struct kobj_type ext4_feat_ktype = {
597597

598598
void ext4_notify_error_sysfs(struct ext4_sb_info *sbi)
599599
{
600-
sysfs_notify(&sbi->s_kobj, NULL, "errors_count");
600+
mutex_lock(&sbi->s_error_notify_mutex);
601+
if (sbi->s_kobj.state_in_sysfs)
602+
sysfs_notify(&sbi->s_kobj, NULL, "errors_count");
603+
mutex_unlock(&sbi->s_error_notify_mutex);
601604
}
602605

603606
static struct kobject *ext4_root;
@@ -610,8 +613,10 @@ int ext4_register_sysfs(struct super_block *sb)
610613
int err;
611614

612615
init_completion(&sbi->s_kobj_unregister);
616+
mutex_lock(&sbi->s_error_notify_mutex);
613617
err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, ext4_root,
614618
"%s", sb->s_id);
619+
mutex_unlock(&sbi->s_error_notify_mutex);
615620
if (err) {
616621
kobject_put(&sbi->s_kobj);
617622
wait_for_completion(&sbi->s_kobj_unregister);
@@ -644,7 +649,10 @@ void ext4_unregister_sysfs(struct super_block *sb)
644649

645650
if (sbi->s_proc)
646651
remove_proc_subtree(sb->s_id, ext4_proc_root);
652+
653+
mutex_lock(&sbi->s_error_notify_mutex);
647654
kobject_del(&sbi->s_kobj);
655+
mutex_unlock(&sbi->s_error_notify_mutex);
648656
}
649657

650658
int __init ext4_init_sysfs(void)

0 commit comments

Comments
 (0)