lkml.org 
[lkml]   [2020]   [Jun]   [23]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.7 416/477] ext4: avoid utf8_strncasecmp() with unstable name
    Date
    From: Eric Biggers <ebiggers@google.com>

    commit 2ce3ee931a097e9720310db3f09c01c825a4580c upstream.

    If the dentry name passed to ->d_compare() fits in dentry::d_iname, then
    it may be concurrently modified by a rename. This can cause undefined
    behavior (possibly out-of-bounds memory accesses or crashes) in
    utf8_strncasecmp(), since fs/unicode/ isn't written to handle strings
    that may be concurrently modified.

    Fix this by first copying the filename to a stack buffer if needed.
    This way we get a stable snapshot of the filename.

    Fixes: b886ee3e778e ("ext4: Support case-insensitive file name lookups")
    Cc: <stable@vger.kernel.org> # v5.2+
    Cc: Al Viro <viro@zeniv.linux.org.uk>
    Cc: Daniel Rosenberg <drosen@google.com>
    Cc: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
    Signed-off-by: Eric Biggers <ebiggers@google.com>
    Reviewed-by: Andreas Dilger <adilger@dilger.ca>
    Link: https://lore.kernel.org/r/20200601200543.59417-1-ebiggers@kernel.org
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

    ---
    fs/ext4/dir.c | 16 ++++++++++++++++
    1 file changed, 16 insertions(+)

    --- a/fs/ext4/dir.c
    +++ b/fs/ext4/dir.c
    @@ -675,6 +675,7 @@ static int ext4_d_compare(const struct d
    struct qstr qstr = {.name = str, .len = len };
    const struct dentry *parent = READ_ONCE(dentry->d_parent);
    const struct inode *inode = READ_ONCE(parent->d_inode);
    + char strbuf[DNAME_INLINE_LEN];

    if (!inode || !IS_CASEFOLDED(inode) ||
    !EXT4_SB(inode->i_sb)->s_encoding) {
    @@ -683,6 +684,21 @@ static int ext4_d_compare(const struct d
    return memcmp(str, name->name, len);
    }

    + /*
    + * If the dentry name is stored in-line, then it may be concurrently
    + * modified by a rename. If this happens, the VFS will eventually retry
    + * the lookup, so it doesn't matter what ->d_compare() returns.
    + * However, it's unsafe to call utf8_strncasecmp() with an unstable
    + * string. Therefore, we have to copy the name into a temporary buffer.
    + */
    + if (len <= DNAME_INLINE_LEN - 1) {
    + memcpy(strbuf, str, len);
    + strbuf[len] = 0;
    + qstr.name = strbuf;
    + /* prevent compiler from optimizing out the temporary buffer */
    + barrier();
    + }
    +
    return ext4_ci_compare(inode, name, &qstr, false);
    }


    \
     
     \ /
      Last update: 2020-06-23 23:27    [W:4.054 / U:0.020 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site