Subject: [PATCH 2504/2944] io_uring: fix racy IOPOLL completions

From: Pavel Begunkov <[email-masked]>
Date: Sun, 6 Dec 2020 22:22:43 +0000

fix #32041927

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: <[email-masked]> # 5.7+
Signed-off-by: Pavel Begunkov <[email-masked]>
Signed-off-by: Jens Axboe <[email-masked]>
Signed-off-by: Jeffle Xu <[email-masked]>
Reviewed-by: Joseph Qi <[email-masked]>
---
 fs/io_uring.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/fs/io_uring.c b/fs/io_uring.c
index ecad074..bfe870f 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -3554,10 +3554,18 @@ static int io_remove_buffers(struct io_kiocb *req, bool force_nonblock)
 	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);
+
+	/* need to hold the lock to complete IOPOLL requests */
+	if (ctx->flags & IORING_SETUP_IOPOLL) {
+		io_req_complete(req, ret);
+		io_ring_submit_unlock(ctx, !force_nonblock);
+	} else {
+		io_ring_submit_unlock(ctx, !force_nonblock);
+		io_req_complete(req, ret);
+	}
+
 	return 0;
 }
 
@@ -3642,10 +3650,18 @@ 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);
+
+	/* need to hold the lock to complete IOPOLL requests */
+	if (ctx->flags & IORING_SETUP_IOPOLL) {
+		io_req_complete(req, ret);
+		io_ring_submit_unlock(ctx, !force_nonblock);
+	} else {
+		io_ring_submit_unlock(ctx, !force_nonblock);
+		io_req_complete(req, ret);
+	}
+
 	return 0;
 }
 
-- 
1.8.3.1