Skip to content

Commit

Permalink
ovl: fix use after free in struct ovl_aio_req
Browse files Browse the repository at this point in the history
ANBZ: torvalds#223

commit 9a25440 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: 2406a30 ("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: Joseph Qi <joseph.qi@linux.alibaba.com>
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Reviewed-by: Xiaoguang Wang <xiaoguang.wang@linux.alibaba.com>
  • Loading branch information
yangerkun authored and josephhz committed Dec 29, 2021
1 parent 25a41e9 commit 6559485
Showing 1 changed file with 14 additions and 2 deletions.
16 changes: 14 additions & 2 deletions fs/overlayfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

struct ovl_aio_req {
struct kiocb iocb;
refcount_t ref;
struct kiocb *orig_iocb;
struct fd fd;
};
Expand Down Expand Up @@ -244,6 +245,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;
Expand All @@ -260,8 +269,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)
Expand Down Expand Up @@ -307,7 +315,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);
ovl_file_accessed(file);
if (ret != -EIOCBQUEUED)
ovl_aio_cleanup_handler(aio_req);
Expand Down Expand Up @@ -371,7 +381,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);
}
Expand Down

0 comments on commit 6559485

Please sign in to comment.