Skip to content

Commit 842d674

Browse files
committed
md: fix mddev uaf while iterating all_mddevs list
jira LE-3262 cve CVE-2025-22126 Rebuild_History Non-Buildable kernel-5.14.0-570.22.1.el9_6 commit-author Yu Kuai <yukuai3@huawei.com> commit 8542870 While iterating all_mddevs list from md_notify_reboot() and md_exit(), list_for_each_entry_safe is used, and this can race with deletint the next mddev, causing UAF: t1: spin_lock //list_for_each_entry_safe(mddev, n, ...) mddev_get(mddev1) // assume mddev2 is the next entry spin_unlock t2: //remove mddev2 ... mddev_free spin_lock list_del spin_unlock kfree(mddev2) mddev_put(mddev1) spin_lock //continue dereference mddev2->all_mddevs The old helper for_each_mddev() actually grab the reference of mddev2 while holding the lock, to prevent from being freed. This problem can be fixed the same way, however, the code will be complex. Hence switch to use list_for_each_entry, in this case mddev_put() can free the mddev1 and it's not safe as well. Refer to md_seq_show(), also factor out a helper mddev_put_locked() to fix this problem. Cc: Christoph Hellwig <hch@lst.de> Link: https://lore.kernel.org/linux-raid/20250220124348.845222-1-yukuai1@huaweicloud.com Fixes: f265143 ("md: stop using for_each_mddev in md_notify_reboot") Fixes: 16648ba ("md: stop using for_each_mddev in md_exit") Reported-and-tested-by: Guillaume Morin <guillaume@morinfr.org> Closes: https://lore.kernel.org/all/Z7Y0SURoA8xwg7vn@bender.morinfr.org/ Signed-off-by: Yu Kuai <yukuai3@huawei.com> Reviewed-by: Christoph Hellwig <hch@lst.de> (cherry picked from commit 8542870) Signed-off-by: Jonathan Maple <jmaple@ciq.com>
1 parent 720fc5b commit 842d674

File tree

1 file changed

+13
-9
lines changed

1 file changed

+13
-9
lines changed

drivers/md/md.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,12 @@ static void __mddev_put(struct mddev *mddev)
608608
queue_work(md_misc_wq, &mddev->del_work);
609609
}
610610

611+
static void mddev_put_locked(struct mddev *mddev)
612+
{
613+
if (atomic_dec_and_test(&mddev->active))
614+
__mddev_put(mddev);
615+
}
616+
611617
void mddev_put(struct mddev *mddev)
612618
{
613619
if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
@@ -8460,9 +8466,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
84608466
if (mddev == list_last_entry(&all_mddevs, struct mddev, all_mddevs))
84618467
status_unused(seq);
84628468

8463-
if (atomic_dec_and_test(&mddev->active))
8464-
__mddev_put(mddev);
8465-
8469+
mddev_put_locked(mddev);
84668470
return 0;
84678471
}
84688472

@@ -9856,11 +9860,11 @@ EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
98569860
static int md_notify_reboot(struct notifier_block *this,
98579861
unsigned long code, void *x)
98589862
{
9859-
struct mddev *mddev, *n;
9863+
struct mddev *mddev;
98609864
int need_delay = 0;
98619865

98629866
spin_lock(&all_mddevs_lock);
9863-
list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
9867+
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
98649868
if (!mddev_get(mddev))
98659869
continue;
98669870
spin_unlock(&all_mddevs_lock);
@@ -9872,8 +9876,8 @@ static int md_notify_reboot(struct notifier_block *this,
98729876
mddev_unlock(mddev);
98739877
}
98749878
need_delay = 1;
9875-
mddev_put(mddev);
98769879
spin_lock(&all_mddevs_lock);
9880+
mddev_put_locked(mddev);
98779881
}
98789882
spin_unlock(&all_mddevs_lock);
98799883

@@ -10206,7 +10210,7 @@ void md_autostart_arrays(int part)
1020610210

1020710211
static __exit void md_exit(void)
1020810212
{
10209-
struct mddev *mddev, *n;
10213+
struct mddev *mddev;
1021010214
int delay = 1;
1021110215

1021210216
unregister_blkdev(MD_MAJOR,"md");
@@ -10227,7 +10231,7 @@ static __exit void md_exit(void)
1022710231
remove_proc_entry("mdstat", NULL);
1022810232

1022910233
spin_lock(&all_mddevs_lock);
10230-
list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
10234+
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
1023110235
if (!mddev_get(mddev))
1023210236
continue;
1023310237
spin_unlock(&all_mddevs_lock);
@@ -10239,8 +10243,8 @@ static __exit void md_exit(void)
1023910243
* the mddev for destruction by a workqueue, and the
1024010244
* destroy_workqueue() below will wait for that to complete.
1024110245
*/
10242-
mddev_put(mddev);
1024310246
spin_lock(&all_mddevs_lock);
10247+
mddev_put_locked(mddev);
1024410248
}
1024510249
spin_unlock(&all_mddevs_lock);
1024610250

0 commit comments

Comments
 (0)