aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorPavel Begunkov <asml.silence@gmail.com>2021-07-20 10:50:44 +0100
committerJens Axboe <axboe@kernel.dk>2021-07-20 07:50:42 -0600
commit46fee9ab02cb24979bbe07631fc3ae95ae08aa3e (patch)
tree77a311f9ff1e7b4d5e66deb1ecb42acf311fff44 /fs
parentio_uring: explicitly count entries for poll reqs (diff)
downloadlinux-dev-46fee9ab02cb24979bbe07631fc3ae95ae08aa3e.tar.xz
linux-dev-46fee9ab02cb24979bbe07631fc3ae95ae08aa3e.zip
io_uring: remove double poll entry on arm failure
__io_queue_proc() can enqueue both poll entries and still fail afterwards, so the callers trying to cancel it should also try to remove the second poll entry (if any). For example, it may leave the request alive referencing a io_uring context but not accessible for cancellation: [ 282.599913][ T1620] task:iou-sqp-23145 state:D stack:28720 pid:23155 ppid: 8844 flags:0x00004004 [ 282.609927][ T1620] Call Trace: [ 282.613711][ T1620] __schedule+0x93a/0x26f0 [ 282.634647][ T1620] schedule+0xd3/0x270 [ 282.638874][ T1620] io_uring_cancel_generic+0x54d/0x890 [ 282.660346][ T1620] io_sq_thread+0xaac/0x1250 [ 282.696394][ T1620] ret_from_fork+0x1f/0x30 Cc: stable@vger.kernel.org Fixes: 18bceab101add ("io_uring: allow POLL_ADD with double poll_wait() users") Reported-and-tested-by: syzbot+ac957324022b7132accf@syzkaller.appspotmail.com Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> Link: https://lore.kernel.org/r/0ec1228fc5eda4cb524eeda857da8efdc43c331c.1626774457.git.asml.silence@gmail.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'fs')
-rw-r--r--fs/io_uring.c2
1 files changed, 2 insertions, 0 deletions
diff --git a/fs/io_uring.c b/fs/io_uring.c
index 6668902cf50c..6486b54a0f62 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -5113,6 +5113,8 @@ static __poll_t __io_arm_poll_handler(struct io_kiocb *req,
ipt->error = -EINVAL;
spin_lock_irq(&ctx->completion_lock);
+ if (ipt->error)
+ io_poll_remove_double(req);
if (likely(poll->head)) {
spin_lock(&poll->head->lock);
if (unlikely(list_empty(&poll->wait.entry))) {