From: Theodore Ts'o tytso@mit.edu
mainline inclusion from mainline-v6.4-rc2 commit 4b3cb1d108bfc2aebb0d7c8a52261a53cf7f5786 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8WZXW
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
--------------------------------
The ext4_dirhash() will *almost* never fail, especially when the hash tree feature was first introduced. However, with the addition of support of encrypted, casefolded file names, that function can most certainly fail today.
So make sure the callers of ext4_dirhash() properly check for failures, and reflect the errors back up to their callers.
Cc: stable@kernel.org Link: https://lore.kernel.org/r/20230506142419.984260-1-tytso@mit.edu Reported-by: syzbot+394aa8a792cb99dbc837@syzkaller.appspotmail.com Reported-by: syzbot+344aaa8697ebd232bfc8@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?id=db56459ea4ac4a676ae4b4678f633e55da005a9... Signed-off-by: Theodore Ts'o tytso@mit.edu
Conflicts: fs/ext4/hash.c fs/ext4/namei.c
Signed-off-by: Baokun Li libaokun1@huawei.com --- fs/ext4/hash.c | 5 ++++- fs/ext4/namei.c | 40 +++++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/fs/ext4/hash.c b/fs/ext4/hash.c index a92eb79de0cc..dce229183b01 100644 --- a/fs/ext4/hash.c +++ b/fs/ext4/hash.c @@ -261,7 +261,10 @@ static int __ext4fs_dirhash(const char *name, int len, break; default: hinfo->hash = 0; - return -1; + hinfo->minor_hash = 0; + pr_warn("invalid/unsupported hash tree version %u", + hinfo->hash_version); + return -EINVAL; } hash = hash & ~1; if (hash == (EXT4_HTREE_EOF_32BIT << 1)) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 4c2162ef63ec..4f1db44acd1c 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -681,7 +681,7 @@ static struct stats dx_show_leaf(struct inode *dir, } if (!fscrypt_has_encryption_key(dir)) { /* Directory is not encrypted */ - ext4fs_dirhash(dir, de->name, + (void) ext4fs_dirhash(dir, de->name, de->name_len, &h); printk("%*.s:(U)%x.%u ", len, name, h.hash, @@ -713,8 +713,9 @@ static struct stats dx_show_leaf(struct inode *dir, name = fname_crypto_str.name; len = fname_crypto_str.len; } - ext4fs_dirhash(dir, de->name, - de->name_len, &h); + (void) ext4fs_dirhash(dir, + de->name, + de->name_len, &h); printk("%*.s:(E)%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); @@ -724,7 +725,8 @@ static struct stats dx_show_leaf(struct inode *dir, #else int len = de->name_len; char *name = de->name; - ext4fs_dirhash(dir, de->name, de->name_len, &h); + (void) ext4fs_dirhash(dir, de->name, + de->name_len, &h); printk("%*.s:%x.%u ", len, name, h.hash, (unsigned) ((char *) de - base)); #endif @@ -814,8 +816,14 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, if (hinfo->hash_version <= DX_HASH_TEA) hinfo->hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; - if (fname && fname_name(fname)) - ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo); + if (fname && fname_name(fname)) { + int ret = ext4fs_dirhash(dir, fname_name(fname), + fname_len(fname), hinfo); + if (ret < 0) { + ret_err = ERR_PTR(ret); + goto fail; + } + } hash = hinfo->hash;
if (root->info.unused_flags & 1) { @@ -1077,7 +1085,12 @@ static int htree_dirblock_to_tree(struct file *dir_file, /* silently ignore the rest of the block */ break; } - ext4fs_dirhash(dir, de->name, de->name_len, hinfo); + err = ext4fs_dirhash(dir, de->name, + de->name_len, hinfo); + if (err < 0) { + count = err; + goto errout; + } if ((hinfo->hash < start_hash) || ((hinfo->hash == start_hash) && (hinfo->minor_hash < start_minor_hash))) @@ -1273,7 +1286,10 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, ((char *)de) - base)) return -EFSCORRUPTED; if (de->name_len && de->inode) { - ext4fs_dirhash(dir, de->name, de->name_len, &h); + int err = ext4fs_dirhash(dir, de->name, + de->name_len, &h); + if (err < 0) + return err; map_tail--; map_tail->hash = h.hash; map_tail->offs = ((char *) de - base)>>2; @@ -2202,7 +2218,13 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname, if (fname->hinfo.hash_version <= DX_HASH_TEA) fname->hinfo.hash_version += EXT4_SB(dir->i_sb)->s_hash_unsigned; fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; - ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), &fname->hinfo); + retval = ext4fs_dirhash(dir, fname_name(fname), + fname_len(fname), &fname->hinfo); + if (retval < 0) { + brelse(bh2); + brelse(bh); + return retval; + }
memset(frames, 0, sizeof(frames)); frame = frames;