lkml.org 
[lkml]   [2020]   [Nov]   [3]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 020/191] fuse: fix page dereference after free
    Date
    From: Miklos Szeredi <mszeredi@redhat.com>

    commit d78092e4937de9ce55edcb4ee4c5e3c707be0190 upstream.

    After unlock_request() pages from the ap->pages[] array may be put (e.g. by
    aborting the connection) and the pages can be freed.

    Prevent use after free by grabbing a reference to the page before calling
    unlock_request().

    The original patch was created by Pradeep P V K.

    Reported-by: Pradeep P V K <ppvk@codeaurora.org>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/fuse/dev.c | 28 ++++++++++++++++++----------
    1 file changed, 18 insertions(+), 10 deletions(-)

    --- a/fs/fuse/dev.c
    +++ b/fs/fuse/dev.c
    @@ -853,15 +853,16 @@ static int fuse_try_move_page(struct fus
    struct page *newpage;
    struct pipe_buffer *buf = cs->pipebufs;

    + get_page(oldpage);
    err = unlock_request(cs->req);
    if (err)
    - return err;
    + goto out_put_old;

    fuse_copy_finish(cs);

    err = pipe_buf_confirm(cs->pipe, buf);
    if (err)
    - return err;
    + goto out_put_old;

    BUG_ON(!cs->nr_segs);
    cs->currbuf = buf;
    @@ -901,7 +902,7 @@ static int fuse_try_move_page(struct fus
    err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);
    if (err) {
    unlock_page(newpage);
    - return err;
    + goto out_put_old;
    }

    get_page(newpage);
    @@ -920,14 +921,19 @@ static int fuse_try_move_page(struct fus
    if (err) {
    unlock_page(newpage);
    put_page(newpage);
    - return err;
    + goto out_put_old;
    }

    unlock_page(oldpage);
    + /* Drop ref for ap->pages[] array */
    put_page(oldpage);
    cs->len = 0;

    - return 0;
    + err = 0;
    +out_put_old:
    + /* Drop ref obtained in this function */
    + put_page(oldpage);
    + return err;

    out_fallback_unlock:
    unlock_page(newpage);
    @@ -936,10 +942,10 @@ out_fallback:
    cs->offset = buf->offset;

    err = lock_request(cs->req);
    - if (err)
    - return err;
    + if (!err)
    + err = 1;

    - return 1;
    + goto out_put_old;
    }

    static int fuse_ref_page(struct fuse_copy_state *cs, struct page *page,
    @@ -951,14 +957,16 @@ static int fuse_ref_page(struct fuse_cop
    if (cs->nr_segs == cs->pipe->buffers)
    return -EIO;

    + get_page(page);
    err = unlock_request(cs->req);
    - if (err)
    + if (err) {
    + put_page(page);
    return err;
    + }

    fuse_copy_finish(cs);

    buf = cs->pipebufs;
    - get_page(page);
    buf->page = page;
    buf->offset = offset;
    buf->len = count;

    \
     
     \ /
      Last update: 2020-11-03 22:02    [W:3.169 / U:1.968 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site