io_uring/net: handle -EINPROGRESS correct for IORING_OP_CONNECT
authorJens Axboe <axboe@kernel.dk>
Wed, 5 Oct 2022 02:29:48 +0000 (20:29 -0600)
committerJens Axboe <axboe@kernel.dk>
Wed, 12 Oct 2022 22:30:56 +0000 (16:30 -0600)
We treat EINPROGRESS like EAGAIN, but if we're retrying post getting
EINPROGRESS, then we just need to check the socket for errors and
terminate the request.

This was exposed on a bluetooth connection request which ends up
taking a while and hitting EINPROGRESS, and yields a CQE result of
-EBADFD because we're retrying a connect on a socket that is now
connected.

Cc: stable@vger.kernel.org
Fixes: 87f80d623c6c ("io_uring: handle connect -EINPROGRESS like -EAGAIN")
Link: https://github.com/axboe/liburing/issues/671
Reported-by: Aidan Sun <aidansun05@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
io_uring/net.c

index caa6a803cb72c083bb9467317550099fd7288dac..8c7226b5bf41381f3ab720c7ea4bd9d2c2a7ccd3 100644 (file)
@@ -46,6 +46,7 @@ struct io_connect {
        struct file                     *file;
        struct sockaddr __user          *addr;
        int                             addr_len;
+       bool                            in_progress;
 };
 
 struct io_sr_msg {
@@ -1386,6 +1387,7 @@ int io_connect_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
 
        conn->addr = u64_to_user_ptr(READ_ONCE(sqe->addr));
        conn->addr_len =  READ_ONCE(sqe->addr2);
+       conn->in_progress = false;
        return 0;
 }
 
@@ -1397,6 +1399,16 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
        int ret;
        bool force_nonblock = issue_flags & IO_URING_F_NONBLOCK;
 
+       if (connect->in_progress) {
+               struct socket *socket;
+
+               ret = -ENOTSOCK;
+               socket = sock_from_file(req->file);
+               if (socket)
+                       ret = sock_error(socket->sk);
+               goto out;
+       }
+
        if (req_has_async_data(req)) {
                io = req->async_data;
        } else {
@@ -1413,13 +1425,17 @@ int io_connect(struct io_kiocb *req, unsigned int issue_flags)
        ret = __sys_connect_file(req->file, &io->address,
                                        connect->addr_len, file_flags);
        if ((ret == -EAGAIN || ret == -EINPROGRESS) && force_nonblock) {
-               if (req_has_async_data(req))
-                       return -EAGAIN;
-               if (io_alloc_async_data(req)) {
-                       ret = -ENOMEM;
-                       goto out;
+               if (ret == -EINPROGRESS) {
+                       connect->in_progress = true;
+               } else {
+                       if (req_has_async_data(req))
+                               return -EAGAIN;
+                       if (io_alloc_async_data(req)) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
+                       memcpy(req->async_data, &__io, sizeof(__io));
                }
-               memcpy(req->async_data, &__io, sizeof(__io));
                return -EAGAIN;
        }
        if (ret == -ERESTARTSYS)