Skip to content

Commit e2a9f73

Browse files
YuKuai-huaweigregkh
authored andcommitted
md: fix mddev uaf while iterating all_mddevs list
commit 8542870 upstream. 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]> Cc: Salvatore Bonaccorso <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6ea2e87 commit e2a9f73

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
@@ -629,6 +629,12 @@ static void __mddev_put(struct mddev *mddev)
629629
queue_work(md_misc_wq, &mddev->del_work);
630630
}
631631

632+
static void mddev_put_locked(struct mddev *mddev)
633+
{
634+
if (atomic_dec_and_test(&mddev->active))
635+
__mddev_put(mddev);
636+
}
637+
632638
void mddev_put(struct mddev *mddev)
633639
{
634640
if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
@@ -8461,9 +8467,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
84618467
if (mddev == list_last_entry(&all_mddevs, struct mddev, all_mddevs))
84628468
status_unused(seq);
84638469

8464-
if (atomic_dec_and_test(&mddev->active))
8465-
__mddev_put(mddev);
8466-
8470+
mddev_put_locked(mddev);
84678471
return 0;
84688472
}
84698473

@@ -9886,11 +9890,11 @@ EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
98869890
static int md_notify_reboot(struct notifier_block *this,
98879891
unsigned long code, void *x)
98889892
{
9889-
struct mddev *mddev, *n;
9893+
struct mddev *mddev;
98909894
int need_delay = 0;
98919895

98929896
spin_lock(&all_mddevs_lock);
9893-
list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
9897+
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
98949898
if (!mddev_get(mddev))
98959899
continue;
98969900
spin_unlock(&all_mddevs_lock);
@@ -9902,8 +9906,8 @@ static int md_notify_reboot(struct notifier_block *this,
99029906
mddev_unlock(mddev);
99039907
}
99049908
need_delay = 1;
9905-
mddev_put(mddev);
99069909
spin_lock(&all_mddevs_lock);
9910+
mddev_put_locked(mddev);
99079911
}
99089912
spin_unlock(&all_mddevs_lock);
99099913

@@ -10236,7 +10240,7 @@ void md_autostart_arrays(int part)
1023610240

1023710241
static __exit void md_exit(void)
1023810242
{
10239-
struct mddev *mddev, *n;
10243+
struct mddev *mddev;
1024010244
int delay = 1;
1024110245

1024210246
unregister_blkdev(MD_MAJOR,"md");
@@ -10257,7 +10261,7 @@ static __exit void md_exit(void)
1025710261
remove_proc_entry("mdstat", NULL);
1025810262

1025910263
spin_lock(&all_mddevs_lock);
10260-
list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
10264+
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
1026110265
if (!mddev_get(mddev))
1026210266
continue;
1026310267
spin_unlock(&all_mddevs_lock);
@@ -10269,8 +10273,8 @@ static __exit void md_exit(void)
1026910273
* the mddev for destruction by a workqueue, and the
1027010274
* destroy_workqueue() below will wait for that to complete.
1027110275
*/
10272-
mddev_put(mddev);
1027310276
spin_lock(&all_mddevs_lock);
10277+
mddev_put_locked(mddev);
1027410278
}
1027510279
spin_unlock(&all_mddevs_lock);
1027610280

0 commit comments

Comments
 (0)