]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
md: fix rcu protection in md_wakeup_thread
authorYun Zhou <yun.zhou@windriver.com>
Wed, 15 Oct 2025 08:32:27 +0000 (16:32 +0800)
committerYu Kuai <yukuai@fnnas.com>
Sat, 8 Nov 2025 08:54:36 +0000 (16:54 +0800)
We attempted to use RCU to protect the pointer 'thread', but directly
passed the value when calling md_wakeup_thread(). This means that the
RCU pointer has been acquired before rcu_read_lock(), which renders
rcu_read_lock() ineffective and could lead to a use-after-free.

Link: https://lore.kernel.org/linux-raid/20251015083227.1079009-1-yun.zhou@windriver.com
Fixes: 446931543982 ("md: protect md_thread with rcu")
Signed-off-by: Yun Zhou <yun.zhou@windriver.com>
Reviewed-by: Li Nan <linan122@huawei.com>
Reviewed-by: Yu Kuai <yukuai@fnnas.com>
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
drivers/md/md.c
drivers/md/md.h

index 8128c8839a0820f05331231c89a6866b5bcede38..6062e0deb61600ab2ead37269e032be70466d4a6 100644 (file)
@@ -99,7 +99,7 @@ static int remove_and_add_spares(struct mddev *mddev,
                                 struct md_rdev *this);
 static void mddev_detach(struct mddev *mddev);
 static void export_rdev(struct md_rdev *rdev, struct mddev *mddev);
-static void md_wakeup_thread_directly(struct md_thread __rcu *thread);
+static void md_wakeup_thread_directly(struct md_thread __rcu **thread);
 
 /*
  * Default number of read corrections we'll attempt on an rdev
@@ -5136,7 +5136,7 @@ static void stop_sync_thread(struct mddev *mddev, bool locked)
         * Thread might be blocked waiting for metadata update which will now
         * never happen
         */
-       md_wakeup_thread_directly(mddev->sync_thread);
+       md_wakeup_thread_directly(&mddev->sync_thread);
        if (work_pending(&mddev->sync_work))
                flush_work(&mddev->sync_work);
 
@@ -8375,22 +8375,21 @@ static int md_thread(void *arg)
        return 0;
 }
 
-static void md_wakeup_thread_directly(struct md_thread __rcu *thread)
+static void md_wakeup_thread_directly(struct md_thread __rcu **thread)
 {
        struct md_thread *t;
 
        rcu_read_lock();
-       t = rcu_dereference(thread);
+       t = rcu_dereference(*thread);
        if (t)
                wake_up_process(t->tsk);
        rcu_read_unlock();
 }
 
-void md_wakeup_thread(struct md_thread __rcu *thread)
+void __md_wakeup_thread(struct md_thread __rcu *thread)
 {
        struct md_thread *t;
 
-       rcu_read_lock();
        t = rcu_dereference(thread);
        if (t) {
                pr_debug("md: waking up MD thread %s.\n", t->tsk->comm);
@@ -8398,9 +8397,8 @@ void md_wakeup_thread(struct md_thread __rcu *thread)
                if (wq_has_sleeper(&t->wqueue))
                        wake_up(&t->wqueue);
        }
-       rcu_read_unlock();
 }
-EXPORT_SYMBOL(md_wakeup_thread);
+EXPORT_SYMBOL(__md_wakeup_thread);
 
 struct md_thread *md_register_thread(void (*run) (struct md_thread *),
                struct mddev *mddev, const char *name)
index 1979c2d4fe89e9763b7e86c148e81627c9c3b391..5d5f780b84477c311886f982084b4972c77567dc 100644 (file)
@@ -882,6 +882,12 @@ struct md_io_clone {
 
 #define THREAD_WAKEUP  0
 
+#define md_wakeup_thread(thread) do {   \
+       rcu_read_lock();                    \
+       __md_wakeup_thread(thread);         \
+       rcu_read_unlock();                  \
+} while (0)
+
 static inline void safe_put_page(struct page *p)
 {
        if (p) put_page(p);
@@ -895,7 +901,7 @@ extern struct md_thread *md_register_thread(
        struct mddev *mddev,
        const char *name);
 extern void md_unregister_thread(struct mddev *mddev, struct md_thread __rcu **threadp);
-extern void md_wakeup_thread(struct md_thread __rcu *thread);
+extern void __md_wakeup_thread(struct md_thread __rcu *thread);
 extern void md_check_recovery(struct mddev *mddev);
 extern void md_reap_sync_thread(struct mddev *mddev);
 extern enum sync_action md_sync_action(struct mddev *mddev);