From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: Baokun Li <libaokun@linux.alibaba.com>, Jan Kara <jack@suse.cz>,
Tejun Heo <tj@kernel.org>,
"Christian Brauner (Amutable)" <brauner@kernel.org>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 5.10.y] writeback: fix race between cgroup_writeback_umount() and inode_switch_wbs()
Date: Thu, 16 Jul 2026 15:49:31 -0400 [thread overview]
Message-ID: <20260716194931.1111583-1-sashal@kernel.org> (raw)
In-Reply-To: <2026071447-raking-bobsled-6a58@gregkh>
From: Baokun Li <libaokun@linux.alibaba.com>
[ Upstream commit cba38ec4cbd3a7b8b942a8d52531a05be8a9ff0d ]
When a container exits, the following BUG_ON() is occasionally triggered:
==================================================================
VFS: Busy inodes after unmount of sdb (ext4)
------------[ cut here ]------------
kernel BUG at fs/super.c:695!
CPU: 3 PID: 6 Comm: containerd-shim Tainted: G OE K 6.6 #1
pstate: 63400009 (nZCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : generic_shutdown_super+0xf0/0x100
lr : generic_shutdown_super+0xf0/0x100
Call trace:
generic_shutdown_super+0xf0/0x100
kill_block_super+0x20/0x48
ext4_kill_sb+0x28/0x60
deactivate_locked_super+0x54/0x130
deactivate_super+0x84/0xa0
cleanup_mnt+0xa4/0x140
__cleanup_mnt+0x18/0x28
task_work_run+0x78/0xe0
do_notify_resume+0x204/0x240
==================================================================
The root cause is a race between cgroup_writeback_umount() and
inode_switch_wbs()/cleanup_offline_cgwb(). There is a window between
inode_prepare_wbs_switch() returning true and the subsequent
wb_queue_isw() call. Following is the process that triggers the issue:
CPU A (umount) | CPU B (writeback)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
inode_switch_wbs/cleanup_offline_cgwb
atomic_inc(&isw_nr_in_flight)
inode_prepare_wbs_switch
-> passes SB_ACTIVE check
__iget(inode)
generic_shutdown_super
sb->s_flags &= ~SB_ACTIVE
cgroup_writeback_umount(sb)
smp_mb()
atomic_read(&isw_nr_in_flight)
rcu_barrier()
-> no pending RCU callbacks
flush_workqueue(isw_wq)
-> nothing queued, returns
evict_inodes(sb)
-> Inode skipped as isw still holds a ref.
sop->put_super(sb)
/* destroys percpu counters */
-> VFS: Busy inodes after unmount!
wb_queue_isw()
queue_work(isw_wq, ...)
/* later in work function */
inode_switch_wbs_work_fn
process_inode_switch_wbs
iput() -> evict
percpu_counter_dec() // UAF!
Fix this by extending the RCU read-side critical section in
inode_switch_wbs() and cleanup_offline_cgwb() to cover from
inode_prepare_wbs_switch() through wb_queue_isw(). Since there is
no sleep in this window, rcu_read_lock() can be used. Then add a
synchronize_rcu() in cgroup_writeback_umount() before the existing
rcu_barrier(), so that all in-flight switchers that have passed the
SB_ACTIVE check have completed queue_work() before flush_workqueue()
is called.
The existing rcu_barrier() is intentionally retained so this fix can
be backported unchanged to stable kernels (5.10.y, 6.6.y, ...) that
still queue switches via queue_rcu_work(). It is a no-op on current
mainline (since commit e1b849cfa6b6 ("writeback: Avoid contention on
wb->list_lock when switching inodes")) and is removed in a follow-up
patch.
Fixes: a1a0e23e4903 ("writeback: flush inode cgroup wb switches instead of pinning super_block")
Cc: stable@vger.kernel.org
Suggested-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/all/mxnjq2l6guusfchvauxr3v7c4bwjasybxlleqbbh4efloeqspz@iqylk76ohufz
Reviewed-by: Jan Kara <jack@suse.cz>
Signed-off-by: Baokun Li <libaokun@linux.alibaba.com>
Link: https://patch.msgid.link/20260521095016.2791354-2-libaokun@linux.alibaba.com
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
fs/fs-writeback.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index f464c9da7bc6a3..1a58b826c74ed3 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -507,12 +507,18 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
atomic_inc(&isw_nr_in_flight);
- /* find and pin the new wb */
+ /*
+ * Paired with synchronize_rcu() in cgroup_writeback_umount():
+ * holding rcu_read_lock across the SB_ACTIVE check, the inode
+ * grab and call_rcu() ensures synchronize_rcu() cannot return
+ * until the switch is queued, so the subsequent rcu_barrier()
+ * and flush_workqueue() will wait for the switch.
+ */
rcu_read_lock();
+ /* find and pin the new wb */
memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys);
if (memcg_css && !css_tryget(memcg_css))
memcg_css = NULL;
- rcu_read_unlock();
if (!memcg_css)
goto out_free;
@@ -542,9 +548,11 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id)
* Let's continue after I_WB_SWITCH is guaranteed to be visible.
*/
call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn);
+ rcu_read_unlock();
return;
out_free:
+ rcu_read_unlock();
atomic_dec(&isw_nr_in_flight);
if (isw->new_wb)
wb_put(isw->new_wb);
@@ -1010,6 +1018,14 @@ int cgroup_writeback_by_id(u64 bdi_id, int memcg_id, unsigned long nr,
void cgroup_writeback_umount(void)
{
if (atomic_read(&isw_nr_in_flight)) {
+ /*
+ * Paired with rcu_read_lock() in inode_switch_wbs().
+ * synchronize_rcu() waits for any in-flight switcher that
+ * already passed the SB_ACTIVE check to finish calling
+ * call_rcu(), so the rcu_barrier() and flush_workqueue()
+ * below will then drain it.
+ */
+ synchronize_rcu();
/*
* Use rcu_barrier() to wait for all pending callbacks to
* ensure that all in-flight wb switches are in the workqueue.
--
2.53.0
prev parent reply other threads:[~2026-07-16 19:49 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-14 14:48 FAILED: patch "[PATCH] writeback: fix race between cgroup_writeback_umount() and" failed to apply to 5.10-stable tree gregkh
2026-07-16 19:49 ` Sasha Levin [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260716194931.1111583-1-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=brauner@kernel.org \
--cc=jack@suse.cz \
--cc=libaokun@linux.alibaba.com \
--cc=stable@vger.kernel.org \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox