mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-07-08 18:13:59 +02:00
io_uring/poll: correctly handle io_poll_add() return value on update
Commit84230ad2d2upstream. When the core of io_uring was updated to handle completions consistently and with fixed return codes, the POLL_REMOVE opcode with updates got slightly broken. If a POLL_ADD is pending and then POLL_REMOVE is used to update the events of that request, if that update causes the POLL_ADD to now trigger, then that completion is lost and a CQE is never posted. Additionally, ensure that if an update does cause an existing POLL_ADD to complete, that the completion value isn't always overwritten with -ECANCELED. For that case, whatever io_poll_add() set the value to should just be retained. Cc: stable@vger.kernel.org Fixes:97b388d70b("io_uring: handle completions in the core") Reported-by: syzbot+641eec6b7af1f62f2b99@syzkaller.appspotmail.com Tested-by: syzbot+641eec6b7af1f62f2b99@syzkaller.appspotmail.com Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
e6011ae848
commit
349ef5d2e7
+19
-7
@@ -6127,7 +6127,7 @@ static int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int io_poll_add(struct io_kiocb *req, unsigned int issue_flags)
|
||||
static int __io_poll_add(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
struct io_poll_iocb *poll = &req->poll;
|
||||
struct io_poll_table ipt;
|
||||
@@ -6139,11 +6139,21 @@ static int io_poll_add(struct io_kiocb *req, unsigned int issue_flags)
|
||||
if (!ret && ipt.error)
|
||||
req_set_fail(req);
|
||||
ret = ret ?: ipt.error;
|
||||
if (ret)
|
||||
if (ret > 0) {
|
||||
__io_req_complete(req, issue_flags, ret, 0);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int io_poll_add(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __io_poll_add(req, issue_flags);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int io_poll_update(struct io_kiocb *req, unsigned int issue_flags)
|
||||
{
|
||||
struct io_ring_ctx *ctx = req->ctx;
|
||||
@@ -6159,6 +6169,7 @@ static int io_poll_update(struct io_kiocb *req, unsigned int issue_flags)
|
||||
ret = preq ? -EALREADY : -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
preq->result = -ECANCELED;
|
||||
spin_unlock(&ctx->completion_lock);
|
||||
|
||||
if (req->poll_update.update_events || req->poll_update.update_user_data) {
|
||||
@@ -6171,16 +6182,17 @@ static int io_poll_update(struct io_kiocb *req, unsigned int issue_flags)
|
||||
if (req->poll_update.update_user_data)
|
||||
preq->user_data = req->poll_update.new_user_data;
|
||||
|
||||
ret2 = io_poll_add(preq, issue_flags);
|
||||
ret2 = __io_poll_add(preq, issue_flags);
|
||||
/* successfully updated, don't complete poll request */
|
||||
if (!ret2)
|
||||
goto out;
|
||||
preq->result = ret2;
|
||||
|
||||
}
|
||||
req_set_fail(preq);
|
||||
io_req_complete(preq, -ECANCELED);
|
||||
if (preq->result < 0)
|
||||
req_set_fail(preq);
|
||||
io_req_complete(preq, preq->result);
|
||||
out:
|
||||
if (ret < 0)
|
||||
req_set_fail(req);
|
||||
/* complete update request, we're done with it */
|
||||
io_req_complete(req, ret);
|
||||
io_ring_submit_unlock(ctx, !(issue_flags & IO_URING_F_NONBLOCK));
|
||||
|
||||
Reference in New Issue
Block a user