[PATCH] super: make iterate_supers_type() deletion-safe
From: Karl Mehltretter
Date: Wed Sep 02 2026 - 21:35:35 EST
iterate_supers_type() drops sb_lock while invoking the callback and keeps
only a passive reference to the current superblock. That reference keeps
the object allocated, but does not keep its s_instances node linked.
After the callback releases s_umount, final teardown can unlink the current
s_instances node. The iterator then advances through a reinitialized node.
With the current hlist it stops without visiting the remaining superblocks.
The unlink moved from generic_shutdown_super() to kill_super_notify(), but
the cursor lifetime has been unsafe since the helper was introduced.
The CIFS DFS lookup can consequently miss a matching superblock and return
-EINVAL.
Walk the global superblock list in reverse and filter it by filesystem
type. A passive reference keeps its s_list node linked, and reverse
traversal preserves newest-first visitation. Superblocks removed from
fs_supers remain on the global list, but teardown marks them SB_DYING
before unlinking them, so the existing filter excludes them.
This broadens the scan from superblocks of one type to all superblocks.
The only in-tree caller is the CIFS DFS lookup, so the broader scan is
limited to that path.
Fixes: 43e15cdbefea ("new helper: iterate_supers_type()")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@xxxxxxxxx>
---
Testing: x86_64 QEMU deterministic KUnit A/B using the actual CIFS lookup
callback over temporary VFS superblocks. Baseline skipped the surviving
match after teardown of the preceding nonmatch and returned -EINVAL. With
only this patch applied, the same test passed.
A real Samba DFS server and the kernel CIFS client were then run in the
same QEMU guest over loopback. Baseline and patch-only kernels both followed
the referrals and completed 20 reconnects while two workers repeatedly
mounted and unmounted independent CIFS superblocks. The patch-only run
successfully mounted, read from, and unmounted CIFS shares 95 times. Kprobes
recorded 110 DFS lookup calls and 60 iterate_supers_type() calls. There were
no unmount failures, kernel warnings, oopses, or sanitizer reports. The
natural network stress did not hit the narrow race on baseline.
Stable is requested because concurrent DFS automount teardown can make a
reconnect lookup miss a live matching superblock.
Backport note: before dc3216b14160 ("super: ensure valid info"), the
s_instances unlink is in generic_shutdown_super(). Before 3ec9800c2d33
("super: convert s_count to refcount_t s_passive"), the passive reference is
named s_count.
fs/super.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/fs/super.c b/fs/super.c
index 05e4431730387..c8accd144bad2 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1026,11 +1026,18 @@ void iterate_supers_type(struct file_system_type *type,
struct super_block *sb, *p = NULL;
spin_lock(&sb_lock);
- hlist_for_each_entry(sb, &type->fs_supers, s_instances) {
+ /*
+ * The passive reference keeps the s_list cursor valid while sb_lock
+ * is dropped. Entries are added at the tail. Walk backwards to retain
+ * newest-first visitation.
+ */
+ list_for_each_entry_reverse(sb, &super_blocks, s_list) {
bool locked;
if (super_flags(sb, SB_DYING))
continue;
+ if (sb->s_type != type)
+ continue;
if (!refcount_inc_not_zero(&sb->s_passive))
continue;
--
2.53.0