lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAAsfc_pafORaG_PrVpOB9GBK+YCjdzJMd2Ww=ya2PbcPkw04+w@mail.gmail.com>
Date: Thu, 13 Nov 2025 19:38:11 +0800
From: liequan che <liequanche@...il.com>
To: Coly Li <colyli@...as.com>
Cc: Kent Overstreet <kent.overstreet@...il.com>, linux-bcache <linux-bcache@...r.kernel.org>, 
	linux-kernel <linux-kernel@...r.kernel.org>
Subject: [PATCH v2] bcache: fix UAF in cached_dev_free and safely flush/destroy

stop writeback thread and rate-update work exactly once across teardown paths,
- Add STOP_THREAD_ONCE() and use it at all three places that stop
  dc->writeback_thread: cached_dev_detach_finish(), cached_dev_free(),
  and the bch_cached_dev_attach() error path.
- In cached_dev_detach_finish(), also clear WB_RUNNING and cancel the
  periodic writeback-rate delayed work to avoid a UAF window after
  detach is initiated.
- Keep the per-dc writeback workqueue flush/destroy in the writeback
  thread exit tail, avoiding double-destroy.
Signed-off-by: cheliequan <cheliequan@...pur.com>
---
 drivers/md/bcache/bcache.h    | 11 +++++++++++
 drivers/md/bcache/super.c     | 14 ++++++--------
 drivers/md/bcache/writeback.c |  7 +++++--
 3 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index 1d33e40d26ea..66dc5dca5c20 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -961,6 +961,17 @@ static inline void wait_for_kthread_stop(void)
        }
 }

+/*
+ * Stop a kthread exactly once by taking ownership of the pointer.
+ * Safe against concurrent callers and against already-stopped threads.
+ */
+#define STOP_THREAD_ONCE(dc, member)                                    \
+       do {                                                             \
+               struct task_struct *t__ = xchg(&(dc)->member, NULL);     \
+               if (t__ && !IS_ERR(t__))                                 \
+               kthread_stop(t__);                                       \
+       } while (0)
+
 /* Forward declarations */

 void bch_count_backing_io_errors(struct cached_dev *dc, struct bio *bio);
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 1492c8552255..b4da0a505d4a 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1143,8 +1143,7 @@ static void cached_dev_detach_finish(struct
work_struct *w)
                cancel_writeback_rate_update_dwork(dc);

        if (!IS_ERR_OR_NULL(dc->writeback_thread)) {
-               kthread_stop(dc->writeback_thread);
-               dc->writeback_thread = NULL;
+               STOP_THREAD_ONCE(dc, writeback_thread);
        }

        mutex_lock(&bch_register_lock);
@@ -1308,8 +1307,9 @@ int bch_cached_dev_attach(struct cached_dev *dc,
struct cache_set *c,
                 * created previously in bch_cached_dev_writeback_start()
                 * have to be stopped manually here.
                 */
-               kthread_stop(dc->writeback_thread);
-               cancel_writeback_rate_update_dwork(dc);
+               if (test_and_clear_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))
+                       cancel_writeback_rate_update_dwork(dc);
+               STOP_THREAD_ONCE(dc, writeback_thread);
                pr_err("Couldn't run cached device %pg\n", dc->bdev);
                return ret;
        }
@@ -1349,10 +1349,8 @@ static CLOSURE_CALLBACK(cached_dev_free)
        if (test_and_clear_bit(BCACHE_DEV_WB_RUNNING, &dc->disk.flags))
                cancel_writeback_rate_update_dwork(dc);

-       if (!IS_ERR_OR_NULL(dc->writeback_thread))
-               kthread_stop(dc->writeback_thread);
-       if (!IS_ERR_OR_NULL(dc->status_update_thread))
-               kthread_stop(dc->status_update_thread);
+       STOP_THREAD_ONCE(dc, writeback_thread);
+       STOP_THREAD_ONCE(dc, status_update_thread);

        mutex_lock(&bch_register_lock);

diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index 302e75f1fc4b..50e67a784acd 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -741,6 +741,7 @@ static int bch_writeback_thread(void *arg)
        struct cached_dev *dc = arg;
        struct cache_set *c = dc->disk.c;
        bool searched_full_index;
+       struct workqueue_struct *wq = NULL;

        bch_ratelimit_reset(&dc->writeback_rate);

@@ -832,8 +833,10 @@ static int bch_writeback_thread(void *arg)
                }
        }

-       if (dc->writeback_write_wq)
-               destroy_workqueue(dc->writeback_write_wq);
+       wq = xchg(&dc->writeback_write_wq, NULL);
+       if (wq) {
+           destroy_workqueue(wq);
+        }

        cached_dev_put(dc);
        wait_for_kthread_stop();
-- 
2.25.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ