lkml.org 
[lkml]   [2018]   [Dec]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Date
    Subject[PATCH 3.16 169/328] ext4: avoid divide by zero fault when deleting corrupted inline directories
    3.16.62-rc1 review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Theodore Ts'o <tytso@mit.edu>

    commit 4d982e25d0bdc83d8c64e66fdeca0b89240b3b85 upstream.

    A specially crafted file system can trick empty_inline_dir() into
    reading past the last valid entry in a inline directory, and then run
    into the end of xattr marker. This will trigger a divide by zero
    fault. Fix this by using the size of the inline directory instead of
    dir->i_size.

    Also clean up error reporting in __ext4_check_dir_entry so that the
    message is clearer and more understandable --- and avoids the division
    by zero trap if the size passed in is zero. (I'm not sure why we
    coded it that way in the first place; printing offset % size is
    actually more confusing and less useful.)

    https://bugzilla.kernel.org/show_bug.cgi?id=200933

    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Reported-by: Wen Xu <wen.xu@gatech.edu>
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    ---
    fs/ext4/dir.c | 20 +++++++++-----------
    fs/ext4/inline.c | 4 +++-
    2 files changed, 12 insertions(+), 12 deletions(-)

    --- a/fs/ext4/dir.c
    +++ b/fs/ext4/dir.c
    @@ -77,7 +77,7 @@ int __ext4_check_dir_entry(const char *f
    else if (unlikely(rlen < EXT4_DIR_REC_LEN(de->name_len)))
    error_msg = "rec_len is too small for name_len";
    else if (unlikely(((char *) de - buf) + rlen > size))
    - error_msg = "directory entry across range";
    + error_msg = "directory entry overrun";
    else if (unlikely(le32_to_cpu(de->inode) >
    le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)))
    error_msg = "inode out of bounds";
    @@ -86,18 +86,16 @@ int __ext4_check_dir_entry(const char *f

    if (filp)
    ext4_error_file(filp, function, line, bh->b_blocknr,
    - "bad entry in directory: %s - offset=%u(%u), "
    - "inode=%u, rec_len=%d, name_len=%d",
    - error_msg, (unsigned) (offset % size),
    - offset, le32_to_cpu(de->inode),
    - rlen, de->name_len);
    + "bad entry in directory: %s - offset=%u, "
    + "inode=%u, rec_len=%d, name_len=%d, size=%d",
    + error_msg, offset, le32_to_cpu(de->inode),
    + rlen, de->name_len, size);
    else
    ext4_error_inode(dir, function, line, bh->b_blocknr,
    - "bad entry in directory: %s - offset=%u(%u), "
    - "inode=%u, rec_len=%d, name_len=%d",
    - error_msg, (unsigned) (offset % size),
    - offset, le32_to_cpu(de->inode),
    - rlen, de->name_len);
    + "bad entry in directory: %s - offset=%u, "
    + "inode=%u, rec_len=%d, name_len=%d, size=%d",
    + error_msg, offset, le32_to_cpu(de->inode),
    + rlen, de->name_len, size);

    return 1;
    }
    --- a/fs/ext4/inline.c
    +++ b/fs/ext4/inline.c
    @@ -1741,6 +1741,7 @@ int empty_inline_dir(struct inode *dir,
    {
    int err, inline_size;
    struct ext4_iloc iloc;
    + size_t inline_len;
    void *inline_pos;
    unsigned int offset;
    struct ext4_dir_entry_2 *de;
    @@ -1768,8 +1769,9 @@ int empty_inline_dir(struct inode *dir,
    goto out;
    }

    + inline_len = ext4_get_inline_size(dir);
    offset = EXT4_INLINE_DOTDOT_SIZE;
    - while (offset < dir->i_size) {
    + while (offset < inline_len) {
    de = ext4_get_inline_entry(dir, &iloc, offset,
    &inline_pos, &inline_size);
    if (ext4_check_dir_entry(dir, NULL, de,
    \
     
     \ /
      Last update: 2018-12-09 23:25    [W:2.553 / U:0.120 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site