Thread (2 messages) 2 messages, 2 authors, 7d ago

Re: [PATCH v2] md: remove legacy async del_gendisk

From: sashiko-bot@kernel.org
Date: 2026-09-22 02:32:07

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] md: Use-After-Free in md_notify_reboot and md_exit due to unsafe list_for_each_entry_safe
- [High] md: Use-After-Free in md_seq_show when mddev_get fails
- [High] md: sysfs self-deadlock in md_attr_store due to synchronous kobject_del
--

commit 882f6ccb12f02b986de958463cf3afb9ffc46b20
Author: Li Youhong [off-list ref]

md: remove legacy async del_gendisk

This commit removes the legacy async del_gendisk path to resolve a lockdep
cycle between major_names_lock, md_misc_wq, and reconfig_mutex. Deletions
are now done synchronously in mddev_unlock() and mddev_put().

Closes: https://syzkaller.appspot.com/bug?extid=68e1f51046d68329940f
quoted hunk ↗ jump to hunk
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 680b34a63cb3d..5d9e76f3256be 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
[ ... ]
 void mddev_put(struct mddev *mddev)
 {
+	bool complete_delete;
+
 	if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
 		return;
 
-	__mddev_put(mddev);
+	complete_delete = __mddev_put(mddev);
 	spin_unlock(&all_mddevs_lock);
+	if (complete_delete)
+		mddev_complete_delete(mddev);
 }
[Severity: High]
Could synchronous kobject_del() on the active path in drivers/md/md.c:mddev_put()
cause a self-deadlock when triggered from a sysfs handler like md_attr_store()?

Writing 'stop' to /sys/block/mdX/md/array_state executes md_attr_store() under
kernfs active protection. The handler calls do_md_stop(), marking the array as
deleted, and then calls mddev_put().

Since mddev_put() now synchronously invokes mddev_complete_delete() ->
kobject_del(), kobject_del() will call kernfs_remove() and block in
kernfs_drain() waiting for the active protection to drop. The calling thread
is holding the active protection itself, causing a deadlock regression.

[ ... ]
quoted hunk ↗ jump to hunk
@@ -9041,8 +9042,13 @@ static int md_seq_show(struct seq_file *seq, void *v)
 	}
 
 	mddev = list_entry(v, struct mddev, all_mddevs);
-	if (!mddev_get(mddev))
+	if (!mddev_get(mddev)) {
+		md_seq_flush_delete(seq);
 		return 0;
+	}
[Severity: High]
Can this sequence in drivers/md/md.c:md_seq_show() lead to a use-after-free
regression when mddev_get() fails?

If mddev_get() fails (because MD_DELETED is set), the current iterator v is
unpinned. The code then calls md_seq_flush_delete(seq), which temporarily
drops all_mddevs_lock to perform delayed deletion of a previous node.

During this unlocked window, the unpinned current node v could be fully
deleted and freed by a concurrent thread. When md_seq_flush_delete() reacquires
the lock and md_seq_show() returns 0, seq_read() will call md_seq_next(),
which dereferences the now-freed v pointer.

[ ... ]
quoted hunk ↗ jump to hunk
@@ -10714,10 +10721,11 @@ EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
 static int md_notify_reboot(struct notifier_block *this,
 			    unsigned long code, void *x)
 {
-	struct mddev *mddev;
+	struct mddev *mddev, *tmp;
+	bool complete_delete;
 
 	spin_lock(&all_mddevs_lock);
-	list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
+	list_for_each_entry_safe(mddev, tmp, &all_mddevs, all_mddevs) {
 		if (!mddev_get(mddev))
 			continue;
 		spin_unlock(&all_mddevs_lock);
[Severity: High]
Does dropping all_mddevs_lock inside list_for_each_entry_safe() in
drivers/md/md.c:md_notify_reboot() expose tmp to concurrent deletion?

Since list_for_each_entry_safe() caches the next pointer into tmp before
executing the loop body, tmp is unpinned and unprotected during the unlocked
window. Because mddev_put() now performs synchronous deletion, a concurrent
array teardown could drop the last reference to tmp and free its memory. When
the lock is reacquired, the loop advances by reading tmp->all_mddevs.next,
which dereferences freed memory.

[ ... ]
quoted hunk ↗ jump to hunk
@@ -11075,20 +11088,15 @@ static __exit void md_exit(void)
 	remove_proc_entry("mdstat", NULL);
 
 	spin_lock(&all_mddevs_lock);
-	list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
+	list_for_each_entry_safe(mddev, tmp, &all_mddevs, all_mddevs) {
 		if (!mddev_get(mddev))
 			continue;
 		spin_unlock(&all_mddevs_lock);
[Severity: High]
Similarly, does dropping the lock here in drivers/md/md.c:md_exit() while
holding an unpinned tmp risk list corruption on module exit?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260922021533.2935172-1-dayou5941@163.com?part=1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help