summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2021-07-26 10:42:56 -0600
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-08-04 12:47:57 +0200
commite95d994d2f8fcff9549433fdcd569300fa3dd123 (patch)
tree05e4f25d4295fb2e43a84be33779f1336c070665
parentee6d50cb1c239e15b6b6ac486fc6ed844955d441 (diff)
io_uring: fix race in unified task_work running
commit 110aa25c3ce417a44e35990cf8ed22383277933a upstream. We use a bit to manage if we need to add the shared task_work, but a list + lock for the pending work. Before aborting a current run of the task_work we check if the list is empty, but we do so without grabbing the lock that protects it. This can lead to races where we think we have nothing left to run, where in practice we could be racing with a task adding new work to the list. If we do hit that race condition, we could be left with work items that need processing, but the shared task_work is not active. Ensure that we grab the lock before checking if the list is empty, so we know if it's safe to exit the run or not. Link: https://lore.kernel.org/io-uring/c6bd5987-e9ae-cd02-49d0-1b3ac1ef65b1@tnonline.net/ Cc: stable@vger.kernel.org # 5.11+ Reported-by: Forza <forza@tnonline.net> Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/io_uring.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/fs/io_uring.c b/fs/io_uring.c
index 0885f04195b2..d465e9997157 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -1899,7 +1899,7 @@ static void tctx_task_work(struct callback_head *cb)
clear_bit(0, &tctx->task_state);
- while (!wq_list_empty(&tctx->task_list)) {
+ while (true) {
struct io_ring_ctx *ctx = NULL;
struct io_wq_work_list list;
struct io_wq_work_node *node;
@@ -1909,6 +1909,9 @@ static void tctx_task_work(struct callback_head *cb)
INIT_WQ_LIST(&tctx->task_list);
spin_unlock_irq(&tctx->task_lock);
+ if (wq_list_empty(&list))
+ break;
+
node = list.first;
while (node) {
struct io_wq_work_node *next = node->next;