io_uring: fix racy IOPOLL completions
authorPavel Begunkov <asml.silence@gmail.com>
Sun, 6 Dec 2020 22:22:43 +0000 (22:22 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 Dec 2020 10:54:03 +0000 (11:54 +0100)
commit 31bff9a51b264df6d144931a6a5f1d6cc815ed4b upstream.

IOPOLL allows buffer remove/provide requests, but they doesn't
synchronise by rules of IOPOLL, namely it have to hold uring_lock.

Cc: <stable@vger.kernel.org> # 5.7+
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/io_uring.c

index 443b021..a1f0c8a 100644 (file)
@@ -3944,11 +3944,17 @@ static int io_remove_buffers(struct io_kiocb *req, bool force_nonblock,
        head = idr_find(&ctx->io_buffer_idr, p->bgid);
        if (head)
                ret = __io_remove_buffers(ctx, head, p->bgid, p->nbufs);
-
-       io_ring_submit_lock(ctx, !force_nonblock);
        if (ret < 0)
                req_set_fail_links(req);
-       __io_req_complete(req, ret, 0, cs);
+
+       /* need to hold the lock to complete IOPOLL requests */
+       if (ctx->flags & IORING_SETUP_IOPOLL) {
+               __io_req_complete(req, ret, 0, cs);
+               io_ring_submit_unlock(ctx, !force_nonblock);
+       } else {
+               io_ring_submit_unlock(ctx, !force_nonblock);
+               __io_req_complete(req, ret, 0, cs);
+       }
        return 0;
 }
 
@@ -4033,10 +4039,17 @@ static int io_provide_buffers(struct io_kiocb *req, bool force_nonblock,
                }
        }
 out:
-       io_ring_submit_unlock(ctx, !force_nonblock);
        if (ret < 0)
                req_set_fail_links(req);
-       __io_req_complete(req, ret, 0, cs);
+
+       /* need to hold the lock to complete IOPOLL requests */
+       if (ctx->flags & IORING_SETUP_IOPOLL) {
+               __io_req_complete(req, ret, 0, cs);
+               io_ring_submit_unlock(ctx, !force_nonblock);
+       } else {
+               io_ring_submit_unlock(ctx, !force_nonblock);
+               __io_req_complete(req, ret, 0, cs);
+       }
        return 0;
 }