FAILED: patch "[PATCH] ext4: fix use-after-free in update_super_work when racing" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x d15e4b0a418537aafa56b2cb80d44add83e83697
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026033054-vascular-atom-023e@gregkh' --subject-prefix 'PATCH 5.15.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From d15e4b0a418537aafa56b2cb80d44add83e83697 Mon Sep 17 00:00:00 2001
From: Jiayuan Chen <[email protected]>
Date: Thu, 19 Mar 2026 20:03:35 +0800
Subject: [PATCH] ext4: fix use-after-free in update_super_work when racing
with umount
Commit b98535d09179 ("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.
fs/ext4/ext4.h +1 -0
@@ -1570,6 +1570,7 @@ struct ext4_sb_info {struct proc_dir_entry *s_proc;struct kobject s_kobj;struct completion s_kobj_unregister; +struct mutex s_error_notify_mutex;/* protects sysfs_notify vs kobject_del */ struct super_block *s_sb;struct buffer_head *s_mmp_bh;
fs/ext4/super.c +1 -0
@@ -5406,6 +5406,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)timer_setup (&sbi->s_err_report, print_daily_error_info,0 );spin_lock_init (&sbi->s_error_lock); +mutex_init (&sbi->s_error_notify_mutex);INIT_WORK (&sbi->s_sb_upd_work, update_super_work); err =ext4_group_desc_init (sb, es, logical_sb_block, &first_not_zeroed);
fs/ext4/sysfs.c +9 -1
@@ -597,7 +597,10 @@ static const struct kobj_type ext4_feat_ktype = {void ext4_notify_error_sysfs (struct ext4_sb_info *sbi) { -sysfs_notify (&sbi->s_kobj,NULL ,"errors_count" ); +mutex_lock (&sbi->s_error_notify_mutex); +if (sbi->s_kobj.state_in_sysfs) +sysfs_notify (&sbi->s_kobj,NULL ,"errors_count" ); +mutex_unlock (&sbi->s_error_notify_mutex); }static struct kobject *ext4_root;
@@ -610,8 +613,10 @@ int ext4_register_sysfs(struct super_block *sb)int err;init_completion (&sbi->s_kobj_unregister); +mutex_lock (&sbi->s_error_notify_mutex); err =kobject_init_and_add (&sbi->s_kobj, &ext4_sb_ktype, ext4_root,"%s" , sb->s_id); +mutex_unlock (&sbi->s_error_notify_mutex);if (err) {kobject_put (&sbi->s_kobj);wait_for_completion (&sbi->s_kobj_unregister);
@@ -644,7 +649,10 @@ void ext4_unregister_sysfs(struct super_block *sb)if (sbi->s_proc)remove_proc_subtree (sb->s_id, ext4_proc_root); + +mutex_lock (&sbi->s_error_notify_mutex);kobject_del (&sbi->s_kobj); +mutex_unlock (&sbi->s_error_notify_mutex); }int __init ext4_init_sysfs (void )
Sasha Levin
maintainerPATCH 1/1 ext4: fix use-after-free in update_super_work when racing with umount
From: Jiayuan Chen <[email protected]>
[ Upstream commit d15e4b0a418537aafa56b2cb80d44add83e83697 ]
Commit b98535d09179 ("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.
--- fs/ext4/ext4.h | 1 + fs/ext4/super.c | 1 + fs/ext4/sysfs.c | 10 +++++++++- 3 files changed, 11 insertions(+), 1 deletion(-)
fs/ext4/ext4.h +1 -0
@@ -1540,6 +1540,7 @@ struct ext4_sb_info {struct proc_dir_entry *s_proc;struct kobject s_kobj;struct completion s_kobj_unregister; +struct mutex s_error_notify_mutex;/* protects sysfs_notify vs kobject_del */ struct super_block *s_sb;struct buffer_head *s_mmp_bh;
fs/ext4/super.c +1 -0
@@ -4621,6 +4621,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)timer_setup (&sbi->s_err_report, print_daily_error_info,0 );spin_lock_init (&sbi->s_error_lock); +mutex_init (&sbi->s_error_notify_mutex);INIT_WORK (&sbi->s_error_work, flush_stashed_error_work);/* Register extent status tree shrinker */
fs/ext4/sysfs.c +9 -1
@@ -513,7 +513,10 @@ static struct kobj_type ext4_feat_ktype = {void ext4_notify_error_sysfs (struct ext4_sb_info *sbi) { -sysfs_notify (&sbi->s_kobj,NULL ,"errors_count" ); +mutex_lock (&sbi->s_error_notify_mutex); +if (sbi->s_kobj.state_in_sysfs) +sysfs_notify (&sbi->s_kobj,NULL ,"errors_count" ); +mutex_unlock (&sbi->s_error_notify_mutex); }static struct kobject *ext4_root;
@@ -526,8 +529,10 @@ int ext4_register_sysfs(struct super_block *sb)int err;init_completion (&sbi->s_kobj_unregister); +mutex_lock (&sbi->s_error_notify_mutex); err =kobject_init_and_add (&sbi->s_kobj, &ext4_sb_ktype, ext4_root,"%s" , sb->s_id); +mutex_unlock (&sbi->s_error_notify_mutex);if (err) {kobject_put (&sbi->s_kobj);wait_for_completion (&sbi->s_kobj_unregister);
@@ -560,7 +565,10 @@ void ext4_unregister_sysfs(struct super_block *sb)if (sbi->s_proc)remove_proc_subtree (sb->s_id, ext4_proc_root); + +mutex_lock (&sbi->s_error_notify_mutex);kobject_del (&sbi->s_kobj); +mutex_unlock (&sbi->s_error_notify_mutex); }int __init ext4_init_sysfs (void )
Signature · 1 line
2.53.0