Skip to content

Commit 8542870

Browse files
YuKuai-huaweiYu Kuai
authored andcommitted
md: fix mddev uaf while iterating all_mddevs list
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 <[email protected]> Link: https://lore.kernel.org/linux-raid/[email protected] 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 <[email protected]> Closes: https://lore.kernel.org/all/[email protected]/ Signed-off-by: Yu Kuai <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 87a8627 commit 8542870

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
@@ -623,6 +623,12 @@ static void __mddev_put(struct mddev *mddev)
623623
queue_work(md_misc_wq, &mddev->del_work);
624624
}
625625

626+
static void mddev_put_locked(struct mddev *mddev)
627+
{
628+
if (atomic_dec_and_test(&mddev->active))
629+
__mddev_put(mddev);
630+
}
631+
626632
void mddev_put(struct mddev *mddev)
627633
{
628634
if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
@@ -8490,9 +8496,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
84908496
if (mddev == list_last_entry(&all_mddevs, struct mddev, all_mddevs))
84918497
status_unused(seq);
84928498

8493-
if (atomic_dec_and_test(&mddev->active))
8494-
__mddev_put(mddev);
8495-
8499+
mddev_put_locked(mddev);
84968500
return 0;
84978501
}
84988502

@@ -9888,11 +9892,11 @@ EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
98889892
static int md_notify_reboot(struct notifier_block *this,
98899893
unsigned long code, void *x)
98909894
{
9891-
struct mddev *mddev, *n;
9895+
struct mddev *mddev;
98929896
int need_delay = 0;
98939897

98949898
spin_lock(&all_mddevs_lock);
9895-
list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
9899+
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
98969900
if (!mddev_get(mddev))
98979901
continue;
98989902
spin_unlock(&all_mddevs_lock);
@@ -9904,8 +9908,8 @@ static int md_notify_reboot(struct notifier_block *this,
99049908
mddev_unlock(mddev);
99059909
}
99069910
need_delay = 1;
9907-
mddev_put(mddev);
99089911
spin_lock(&all_mddevs_lock);
9912+
mddev_put_locked(mddev);
99099913
}
99109914
spin_unlock(&all_mddevs_lock);
99119915

@@ -10238,7 +10242,7 @@ void md_autostart_arrays(int part)
1023810242

1023910243
static __exit void md_exit(void)
1024010244
{
10241-
struct mddev *mddev, *n;
10245+
struct mddev *mddev;
1024210246
int delay = 1;
1024310247

1024410248
unregister_blkdev(MD_MAJOR,"md");
@@ -10259,7 +10263,7 @@ static __exit void md_exit(void)
1025910263
remove_proc_entry("mdstat", NULL);
1026010264

1026110265
spin_lock(&all_mddevs_lock);
10262-
list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
10266+
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
1026310267
if (!mddev_get(mddev))
1026410268
continue;
1026510269
spin_unlock(&all_mddevs_lock);
@@ -10271,8 +10275,8 @@ static __exit void md_exit(void)
1027110275
* the mddev for destruction by a workqueue, and the
1027210276
* destroy_workqueue() below will wait for that to complete.
1027310277
*/
10274-
mddev_put(mddev);
1027510278
spin_lock(&all_mddevs_lock);
10279+
mddev_put_locked(mddev);
1027610280
}
1027710281
spin_unlock(&all_mddevs_lock);
1027810282

0 commit comments

Comments
 (0)