io_uring: don't attempt re-add of multishot poll request if racing
authorJens Axboe <axboe@kernel.dk>
Tue, 6 Apr 2021 15:49:31 +0000 (09:49 -0600)
committerJens Axboe <axboe@kernel.dk>
Mon, 12 Apr 2021 01:30:35 +0000 (19:30 -0600)
We currently allow racy updates to multishot requests, but we can end up
double adding the poll request if both completion and update does it.
Ensure that we skip re-add on the update side if someone else is
completing it.

Fixes: b69de288e913 ("io_uring: allow events and user_data update of running poll requests")
Reported-by: Joakim Hassila <joj@mac.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io_uring.c

index ceb5ddd368261746ec78bc84778a0ab5310f910c..0fcb0f477d9ee9fe2f83fb4e47e6c7b82faf27fa 100644 (file)
@@ -5432,6 +5432,7 @@ static int io_poll_update(struct io_kiocb *req)
 {
        struct io_ring_ctx *ctx = req->ctx;
        struct io_kiocb *preq;
+       bool completing;
        int ret;
 
        spin_lock_irq(&ctx->completion_lock);
@@ -5444,17 +5445,22 @@ static int io_poll_update(struct io_kiocb *req)
                ret = -EACCES;
                goto err;
        }
-       if (!__io_poll_remove_one(preq, &preq->poll, false)) {
-               if (preq->poll.events & EPOLLONESHOT) {
-                       ret = -EALREADY;
-                       goto err;
-               }
+
+       /*
+        * Don't allow racy completion with singleshot, as we cannot safely
+        * update those. For multishot, if we're racing with completion, just
+        * let completion re-add it.
+        */
+       completing = !__io_poll_remove_one(preq, &preq->poll, false);
+       if (completing && (preq->poll.events & EPOLLONESHOT)) {
+               ret = -EALREADY;
+               goto err;
        }
        /* we now have a detached poll request. reissue. */
        ret = 0;
 err:
-       spin_unlock_irq(&ctx->completion_lock);
        if (ret < 0) {
+               spin_unlock_irq(&ctx->completion_lock);
                req_set_fail_links(req);
                io_req_complete(req, ret);
                return 0;
@@ -5468,13 +5474,17 @@ err:
        if (req->poll.update_user_data)
                preq->user_data = req->poll.new_user_data;
 
+       spin_unlock_irq(&ctx->completion_lock);
+
        /* complete update request, we're done with it */
        io_req_complete(req, ret);
 
-       ret = __io_poll_add(preq);
-       if (ret < 0) {
-               req_set_fail_links(preq);
-               io_req_complete(preq, ret);
+       if (!completing) {
+               ret = __io_poll_add(preq);
+               if (ret < 0) {
+                       req_set_fail_links(preq);
+                       io_req_complete(preq, ret);
+               }
        }
        return 0;
 }