lkml.org 
[lkml]   [2022]   [Jul]   [1]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    Subject[PATCH v4 44/45] mm: fs: initialize fsdata passed to write_begin/write_end interface
    From
    Functions implementing the a_ops->write_end() interface accept the
    `void *fsdata` parameter that is supposed to be initialized by the
    corresponding a_ops->write_begin() (which accepts `void **fsdata`).

    However not all a_ops->write_begin() implementations initialize `fsdata`
    unconditionally, so it may get passed uninitialized to a_ops->write_end(),
    resulting in undefined behavior.

    Fix this by initializing fsdata with NULL before the call to
    write_begin(), rather than doing so in all possible a_ops
    implementations.

    This patch covers only the following cases found by running x86 KMSAN
    under syzkaller:

    - generic_perform_write()
    - cont_expand_zero() and generic_cont_expand_simple()
    - page_symlink()

    Other cases of passing uninitialized fsdata may persist in the codebase.

    Signed-off-by: Alexander Potapenko <glider@google.com>
    ---
    Link: https://linux-review.googlesource.com/id/I414f0ee3a164c9c335d91d82ce4558f6f2841471
    ---
    fs/buffer.c | 4 ++--
    fs/namei.c | 2 +-
    mm/filemap.c | 2 +-
    3 files changed, 4 insertions(+), 4 deletions(-)

    diff --git a/fs/buffer.c b/fs/buffer.c
    index 898c7f301b1b9..d014009cff941 100644
    --- a/fs/buffer.c
    +++ b/fs/buffer.c
    @@ -2349,7 +2349,7 @@ int generic_cont_expand_simple(struct inode *inode, loff_t size)
    struct address_space *mapping = inode->i_mapping;
    const struct address_space_operations *aops = mapping->a_ops;
    struct page *page;
    - void *fsdata;
    + void *fsdata = NULL;
    int err;

    err = inode_newsize_ok(inode, size);
    @@ -2375,7 +2375,7 @@ static int cont_expand_zero(struct file *file, struct address_space *mapping,
    const struct address_space_operations *aops = mapping->a_ops;
    unsigned int blocksize = i_blocksize(inode);
    struct page *page;
    - void *fsdata;
    + void *fsdata = NULL;
    pgoff_t index, curidx;
    loff_t curpos;
    unsigned zerofrom, offset, len;
    diff --git a/fs/namei.c b/fs/namei.c
    index 6b39dfd3b41bc..5e3ff9d65f502 100644
    --- a/fs/namei.c
    +++ b/fs/namei.c
    @@ -5051,7 +5051,7 @@ int page_symlink(struct inode *inode, const char *symname, int len)
    const struct address_space_operations *aops = mapping->a_ops;
    bool nofs = !mapping_gfp_constraint(mapping, __GFP_FS);
    struct page *page;
    - void *fsdata;
    + void *fsdata = NULL;
    int err;
    unsigned int flags;

    diff --git a/mm/filemap.c b/mm/filemap.c
    index ffdfbc8b0e3ca..72467f00f1916 100644
    --- a/mm/filemap.c
    +++ b/mm/filemap.c
    @@ -3753,7 +3753,7 @@ ssize_t generic_perform_write(struct kiocb *iocb, struct iov_iter *i)
    unsigned long offset; /* Offset into pagecache page */
    unsigned long bytes; /* Bytes to write to page */
    size_t copied; /* Bytes copied from user */
    - void *fsdata;
    + void *fsdata = NULL;

    offset = (pos & (PAGE_SIZE - 1));
    bytes = min_t(unsigned long, PAGE_SIZE - offset,
    --
    2.37.0.rc0.161.g10f37bed90-goog
    \
     
     \ /
      Last update: 2022-07-01 16:31    [W:7.085 / U:0.028 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site