lkml.org 
[lkml]   [2022]   [Jun]   [13]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.14 107/218] ext4: verify dir block before splitting it
    Date
    From: Jan Kara <jack@suse.cz>

    commit 46c116b920ebec58031f0a78c5ea9599b0d2a371 upstream.

    Before splitting a directory block verify its directory entries are sane
    so that the splitting code does not access memory it should not.

    Cc: stable@vger.kernel.org
    Signed-off-by: Jan Kara <jack@suse.cz>
    Link: https://lore.kernel.org/r/20220518093332.13986-1-jack@suse.cz
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    fs/ext4/namei.c | 32 +++++++++++++++++++++-----------
    1 file changed, 21 insertions(+), 11 deletions(-)

    --- a/fs/ext4/namei.c
    +++ b/fs/ext4/namei.c
    @@ -271,9 +271,9 @@ static struct dx_frame *dx_probe(struct
    struct dx_hash_info *hinfo,
    struct dx_frame *frame);
    static void dx_release(struct dx_frame *frames);
    -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
    - unsigned blocksize, struct dx_hash_info *hinfo,
    - struct dx_map_entry map[]);
    +static int dx_make_map(struct inode *dir, struct buffer_head *bh,
    + struct dx_hash_info *hinfo,
    + struct dx_map_entry *map_tail);
    static void dx_sort_map(struct dx_map_entry *map, unsigned count);
    static struct ext4_dir_entry_2 *dx_move_dirents(char *from, char *to,
    struct dx_map_entry *offsets, int count, unsigned blocksize);
    @@ -1202,15 +1202,23 @@ static inline int search_dirblock(struct
    * Create map of hash values, offsets, and sizes, stored at end of block.
    * Returns number of entries mapped.
    */
    -static int dx_make_map(struct inode *dir, struct ext4_dir_entry_2 *de,
    - unsigned blocksize, struct dx_hash_info *hinfo,
    +static int dx_make_map(struct inode *dir, struct buffer_head *bh,
    + struct dx_hash_info *hinfo,
    struct dx_map_entry *map_tail)
    {
    int count = 0;
    - char *base = (char *) de;
    + struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)bh->b_data;
    + unsigned int buflen = bh->b_size;
    + char *base = bh->b_data;
    struct dx_hash_info h = *hinfo;

    - while ((char *) de < base + blocksize) {
    + if (ext4_has_metadata_csum(dir->i_sb))
    + buflen -= sizeof(struct ext4_dir_entry_tail);
    +
    + while ((char *) de < base + buflen) {
    + if (ext4_check_dir_entry(dir, NULL, de, bh, base, buflen,
    + ((char *)de) - base))
    + return -EFSCORRUPTED;
    if (de->name_len && de->inode) {
    ext4fs_dirhash(de->name, de->name_len, &h);
    map_tail--;
    @@ -1220,8 +1228,7 @@ static int dx_make_map(struct inode *dir
    count++;
    cond_resched();
    }
    - /* XXX: do we need to check rec_len == 0 case? -Chris */
    - de = ext4_next_entry(de, blocksize);
    + de = ext4_next_entry(de, dir->i_sb->s_blocksize);
    }
    return count;
    }
    @@ -1737,8 +1744,11 @@ static struct ext4_dir_entry_2 *do_split

    /* create map in the end of data2 block */
    map = (struct dx_map_entry *) (data2 + blocksize);
    - count = dx_make_map(dir, (struct ext4_dir_entry_2 *) data1,
    - blocksize, hinfo, map);
    + count = dx_make_map(dir, *bh, hinfo, map);
    + if (count < 0) {
    + err = count;
    + goto journal_error;
    + }
    map -= count;
    dx_sort_map(map, count);
    /* Ensure that neither split block is over half full */

    \
     
     \ /
      Last update: 2022-06-13 12:52    [W:4.230 / U:0.264 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site