lkml.org 
[lkml]   [2021]   [Nov]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.15 056/917] fuse: fix page stealing
    Date
    From: Miklos Szeredi <mszeredi@redhat.com>

    commit 712a951025c0667ff00b25afc360f74e639dfabe upstream.

    It is possible to trigger a crash by splicing anon pipe bufs to the fuse
    device.

    The reason for this is that anon_pipe_buf_release() will reuse buf->page if
    the refcount is 1, but that page might have already been stolen and its
    flags modified (e.g. PG_lru added).

    This happens in the unlikely case of fuse_dev_splice_write() getting around
    to calling pipe_buf_release() after a page has been stolen, added to the
    page cache and removed from the page cache.

    Fix by calling pipe_buf_release() right after the page was inserted into
    the page cache. In this case the page has an elevated refcount so any
    release function will know that the page isn't reusable.

    Reported-by: Frank Dinoff <fdinoff@google.com>
    Link: https://lore.kernel.org/r/CAAmZXrsGg2xsP1CK+cbuEMumtrqdvD-NKnWzhNcvn71RV3c1yw@mail.gmail.com/
    Fixes: dd3bb14f44a6 ("fuse: support splice() writing to fuse device")
    Cc: <stable@vger.kernel.org> # v2.6.35
    Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    fs/fuse/dev.c | 14 ++++++++++++--
    1 file changed, 12 insertions(+), 2 deletions(-)

    --- a/fs/fuse/dev.c
    +++ b/fs/fuse/dev.c
    @@ -847,6 +847,12 @@ static int fuse_try_move_page(struct fus

    replace_page_cache_page(oldpage, newpage);

    + /*
    + * Release while we have extra ref on stolen page. Otherwise
    + * anon_pipe_buf_release() might think the page can be reused.
    + */
    + pipe_buf_release(cs->pipe, buf);
    +
    get_page(newpage);

    if (!(buf->flags & PIPE_BUF_FLAG_LRU))
    @@ -2031,8 +2037,12 @@ static ssize_t fuse_dev_splice_write(str

    pipe_lock(pipe);
    out_free:
    - for (idx = 0; idx < nbuf; idx++)
    - pipe_buf_release(pipe, &bufs[idx]);
    + for (idx = 0; idx < nbuf; idx++) {
    + struct pipe_buffer *buf = &bufs[idx];
    +
    + if (buf->ops)
    + pipe_buf_release(pipe, buf);
    + }
    pipe_unlock(pipe);

    kvfree(bufs);

    \
     
     \ /
      Last update: 2021-11-16 02:15    [W:2.223 / U:0.052 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site