Commit fd7d6de2 authored by Jens Axboe's avatar Jens Axboe

io_uring: don't recurse on tsk->sighand->siglock with signalfd

If an application is doing reads on signalfd, and we arm the poll handler
because there's no data available, then the wakeup can recurse on the
tasks sighand->siglock as the signal delivery from task_work_add() will
use TWA_SIGNAL and that attempts to lock it again.

We can detect the signalfd case pretty easily by comparing the poll->head
wait_queue_head_t with the target task signalfd wait queue. Just use
normal task wakeup for this case.

Cc: stable@vger.kernel.org # v5.7+
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 867a23ea
...@@ -1746,7 +1746,8 @@ static struct io_kiocb *io_req_find_next(struct io_kiocb *req) ...@@ -1746,7 +1746,8 @@ static struct io_kiocb *io_req_find_next(struct io_kiocb *req)
return __io_req_find_next(req); return __io_req_find_next(req);
} }
static int io_req_task_work_add(struct io_kiocb *req, struct callback_head *cb) static int io_req_task_work_add(struct io_kiocb *req, struct callback_head *cb,
bool twa_signal_ok)
{ {
struct task_struct *tsk = req->task; struct task_struct *tsk = req->task;
struct io_ring_ctx *ctx = req->ctx; struct io_ring_ctx *ctx = req->ctx;
...@@ -1759,7 +1760,7 @@ static int io_req_task_work_add(struct io_kiocb *req, struct callback_head *cb) ...@@ -1759,7 +1760,7 @@ static int io_req_task_work_add(struct io_kiocb *req, struct callback_head *cb)
* will do the job. * will do the job.
*/ */
notify = 0; notify = 0;
if (!(ctx->flags & IORING_SETUP_SQPOLL)) if (!(ctx->flags & IORING_SETUP_SQPOLL) && twa_signal_ok)
notify = TWA_SIGNAL; notify = TWA_SIGNAL;
ret = task_work_add(tsk, cb, notify); ret = task_work_add(tsk, cb, notify);
...@@ -1819,7 +1820,7 @@ static void io_req_task_queue(struct io_kiocb *req) ...@@ -1819,7 +1820,7 @@ static void io_req_task_queue(struct io_kiocb *req)
init_task_work(&req->task_work, io_req_task_submit); init_task_work(&req->task_work, io_req_task_submit);
percpu_ref_get(&req->ctx->refs); percpu_ref_get(&req->ctx->refs);
ret = io_req_task_work_add(req, &req->task_work); ret = io_req_task_work_add(req, &req->task_work, true);
if (unlikely(ret)) { if (unlikely(ret)) {
struct task_struct *tsk; struct task_struct *tsk;
...@@ -2322,7 +2323,7 @@ static bool io_rw_reissue(struct io_kiocb *req, long res) ...@@ -2322,7 +2323,7 @@ static bool io_rw_reissue(struct io_kiocb *req, long res)
init_task_work(&req->task_work, io_rw_resubmit); init_task_work(&req->task_work, io_rw_resubmit);
percpu_ref_get(&req->ctx->refs); percpu_ref_get(&req->ctx->refs);
ret = io_req_task_work_add(req, &req->task_work); ret = io_req_task_work_add(req, &req->task_work, true);
if (!ret) if (!ret)
return true; return true;
#endif #endif
...@@ -3044,7 +3045,7 @@ static int io_async_buf_func(struct wait_queue_entry *wait, unsigned mode, ...@@ -3044,7 +3045,7 @@ static int io_async_buf_func(struct wait_queue_entry *wait, unsigned mode,
/* submit ref gets dropped, acquire a new one */ /* submit ref gets dropped, acquire a new one */
refcount_inc(&req->refs); refcount_inc(&req->refs);
ret = io_req_task_work_add(req, &req->task_work); ret = io_req_task_work_add(req, &req->task_work, true);
if (unlikely(ret)) { if (unlikely(ret)) {
struct task_struct *tsk; struct task_struct *tsk;
...@@ -4566,6 +4567,7 @@ struct io_poll_table { ...@@ -4566,6 +4567,7 @@ struct io_poll_table {
static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll, static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll,
__poll_t mask, task_work_func_t func) __poll_t mask, task_work_func_t func)
{ {
bool twa_signal_ok;
int ret; int ret;
/* for instances that support it check for an event match first: */ /* for instances that support it check for an event match first: */
...@@ -4580,13 +4582,21 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll, ...@@ -4580,13 +4582,21 @@ static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll,
init_task_work(&req->task_work, func); init_task_work(&req->task_work, func);
percpu_ref_get(&req->ctx->refs); percpu_ref_get(&req->ctx->refs);
/*
* If we using the signalfd wait_queue_head for this wakeup, then
* it's not safe to use TWA_SIGNAL as we could be recursing on the
* tsk->sighand->siglock on doing the wakeup. Should not be needed
* either, as the normal wakeup will suffice.
*/
twa_signal_ok = (poll->head != &req->task->sighand->signalfd_wqh);
/* /*
* If this fails, then the task is exiting. When a task exits, the * If this fails, then the task is exiting. When a task exits, the
* work gets canceled, so just cancel this request as well instead * work gets canceled, so just cancel this request as well instead
* of executing it. We can't safely execute it anyway, as we may not * of executing it. We can't safely execute it anyway, as we may not
* have the needed state needed for it anyway. * have the needed state needed for it anyway.
*/ */
ret = io_req_task_work_add(req, &req->task_work); ret = io_req_task_work_add(req, &req->task_work, twa_signal_ok);
if (unlikely(ret)) { if (unlikely(ret)) {
struct task_struct *tsk; struct task_struct *tsk;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment