]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
ovl: fix use after free in struct ovl_aio_req
authoryangerkun <yangerkun@huawei.com>
Thu, 30 Sep 2021 03:22:28 +0000 (11:22 +0800)
committerAndrea Righi <andrea.righi@canonical.com>
Tue, 4 Jan 2022 08:48:11 +0000 (09:48 +0100)
BugLink: https://bugs.launchpad.net/bugs/1951822
commit 9a254403760041528bc8f69fe2f5e1ef86950991 upstream.

Example for triggering use after free in a overlay on ext4 setup:

aio_read
  ovl_read_iter
    vfs_iter_read
      ext4_file_read_iter
        ext4_dio_read_iter
          iomap_dio_rw -> -EIOCBQUEUED
          /*
   * Here IO is completed in a separate thread,
   * ovl_aio_cleanup_handler() frees aio_req which has iocb embedded
   */
          file_accessed(iocb->ki_filp); /**BOOM**/

Fix by introducing a refcount in ovl_aio_req similarly to aio_kiocb.  This
guarantees that iocb is only freed after vfs_read/write_iter() returns on
underlying fs.

Fixes: 2406a307ac7d ("ovl: implement async IO routines")
Signed-off-by: yangerkun <yangerkun@huawei.com>
Link: https://lore.kernel.org/r/20210930032228.3199690-3-yangerkun@huawei.com/
Cc: <stable@vger.kernel.org> # v5.6
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
fs/overlayfs/file.c

index 3cd332cb77c9f4af254be48216d441402029c17c..d0c96ca0202eb3d9541c8e0920f26bb723ce0d60 100644 (file)
@@ -17,6 +17,7 @@
 
 struct ovl_aio_req {
        struct kiocb iocb;
+       refcount_t ref;
        struct kiocb *orig_iocb;
        struct fd fd;
 };
@@ -254,6 +255,14 @@ static rwf_t ovl_iocb_to_rwf(int ifl)
        return flags;
 }
 
+static inline void ovl_aio_put(struct ovl_aio_req *aio_req)
+{
+       if (refcount_dec_and_test(&aio_req->ref)) {
+               fdput(aio_req->fd);
+               kmem_cache_free(ovl_aio_request_cachep, aio_req);
+       }
+}
+
 static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
 {
        struct kiocb *iocb = &aio_req->iocb;
@@ -270,8 +279,7 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
        }
 
        orig_iocb->ki_pos = iocb->ki_pos;
-       fdput(aio_req->fd);
-       kmem_cache_free(ovl_aio_request_cachep, aio_req);
+       ovl_aio_put(aio_req);
 }
 
 static void ovl_aio_rw_complete(struct kiocb *iocb, long res, long res2)
@@ -321,7 +329,9 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
                aio_req->orig_iocb = iocb;
                kiocb_clone(&aio_req->iocb, iocb, real.file);
                aio_req->iocb.ki_complete = ovl_aio_rw_complete;
+               refcount_set(&aio_req->ref, 2);
                ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter);
+               ovl_aio_put(aio_req);
                if (ret != -EIOCBQUEUED)
                        ovl_aio_cleanup_handler(aio_req);
        }
@@ -392,7 +402,9 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
                kiocb_clone(&aio_req->iocb, iocb, real.file);
                aio_req->iocb.ki_flags = ifl;
                aio_req->iocb.ki_complete = ovl_aio_rw_complete;
+               refcount_set(&aio_req->ref, 2);
                ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter);
+               ovl_aio_put(aio_req);
                if (ret != -EIOCBQUEUED)
                        ovl_aio_cleanup_handler(aio_req);
        }