summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2020-08-11 17:04:14 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-08-19 09:27:09 +0300
commit960ea38bfe7a0edd403d6a9e2d358018b4ce3bfa (patch)
treeb1c45acfc0b60e19b003377647c3022898d969d3
parenta4418e11cf9b617b0d433ec9507291dc785d3501 (diff)
downloadlinux-960ea38bfe7a0edd403d6a9e2d358018b4ce3bfa.tar.xz
io_uring: hold 'ctx' reference around task_work queue + execute
commit 6d816e088c359866f9867057e04f244c608c42fe upstream. We're holding the request reference, but we need to go one higher to ensure that the ctx remains valid after the request has finished. If the ring is closed with pending task_work inflight, and the given io_kiocb finishes sync during issue, then we need a reference to the ring itself around the task_work execution cycle. Cc: stable@vger.kernel.org # v5.7+ Reported-by: syzbot+9b260fc33297966f5a8e@syzkaller.appspotmail.com Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/io_uring.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/fs/io_uring.c b/fs/io_uring.c
index e42c7583ee5e..0918fae6270c 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -4140,6 +4140,8 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll,
tsk = req->task;
req->result = mask;
init_task_work(&req->task_work, func);
+ percpu_ref_get(&req->ctx->refs);
+
/*
* If this fails, then the task is exiting. When a task exits, the
* work gets canceled, so just cancel this request as well instead
@@ -4239,6 +4241,7 @@ static void io_poll_task_handler(struct io_kiocb *req, struct io_kiocb **nxt)
static void io_poll_task_func(struct callback_head *cb)
{
struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work);
+ struct io_ring_ctx *ctx = req->ctx;
struct io_kiocb *nxt = NULL;
io_poll_task_handler(req, &nxt);
@@ -4249,6 +4252,7 @@ static void io_poll_task_func(struct callback_head *cb)
__io_queue_sqe(nxt, NULL);
mutex_unlock(&ctx->uring_lock);
}
+ percpu_ref_put(&ctx->refs);
}
static int io_poll_double_wake(struct wait_queue_entry *wait, unsigned mode,
@@ -4365,6 +4369,7 @@ static void io_async_task_func(struct callback_head *cb)
if (io_poll_rewait(req, &apoll->poll)) {
spin_unlock_irq(&ctx->completion_lock);
+ percpu_ref_put(&ctx->refs);
return;
}
@@ -4402,6 +4407,7 @@ end_req:
req_set_fail_links(req);
io_double_put_req(req);
}
+ percpu_ref_put(&ctx->refs);
}
static int io_async_wake(struct wait_queue_entry *wait, unsigned mode, int sync,