io_uring: fix fs->users overflow
authorPavel Begunkov <asml.silence@gmail.com>
Thu, 14 Apr 2022 07:50:50 +0000 (08:50 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Apr 2022 12:18:41 +0000 (14:18 +0200)
There is a bunch of cases where we can grab req->fs but not put it, this
can be used to cause a controllable overflow with further implications.
Release req->fs in the request free path and make sure we zero the field
to be sure we don't do it twice.

Fixes: cac68d12c531 ("io_uring: grab ->fs as part of async offload")
Reported-by: Bing-Jhong Billy Jheng <billy@starlabs.sg>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/io_uring.c

index 478df7e10767a660d0a51b49fafd0c7d868d22c7..e73969fa96bcb088f3ea005e7191c086cc7347a6 100644 (file)
@@ -438,6 +438,22 @@ static struct io_ring_ctx *io_ring_ctx_alloc(struct io_uring_params *p)
        return ctx;
 }
 
+static void io_req_put_fs(struct io_kiocb *req)
+{
+       struct fs_struct *fs = req->fs;
+
+       if (!fs)
+               return;
+
+       spin_lock(&req->fs->lock);
+       if (--fs->users)
+               fs = NULL;
+       spin_unlock(&req->fs->lock);
+       if (fs)
+               free_fs_struct(fs);
+       req->fs = NULL;
+}
+
 static inline bool __io_sequence_defer(struct io_ring_ctx *ctx,
                                       struct io_kiocb *req)
 {
@@ -695,6 +711,7 @@ static void io_free_req_many(struct io_ring_ctx *ctx, void **reqs, int *nr)
 
 static void __io_free_req(struct io_kiocb *req)
 {
+       io_req_put_fs(req);
        if (req->file && !(req->flags & REQ_F_FIXED_FILE))
                fput(req->file);
        percpu_ref_put(&req->ctx->refs);
@@ -1701,16 +1718,7 @@ static int io_send_recvmsg(struct io_kiocb *req, const struct io_uring_sqe *sqe,
                        ret = -EINTR;
        }
 
-       if (req->fs) {
-               struct fs_struct *fs = req->fs;
-
-               spin_lock(&req->fs->lock);
-               if (--fs->users)
-                       fs = NULL;
-               spin_unlock(&req->fs->lock);
-               if (fs)
-                       free_fs_struct(fs);
-       }
+       io_req_put_fs(req);
        io_cqring_add_event(req->ctx, sqe->user_data, ret);
        io_put_req(req);
        return 0;