[<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