Commit bf30b3ce authored by Theodore Ts'o's avatar Theodore Ts'o Committed by Baokun Li
Browse files

ext4: improve error handling from ext4_dirhash()

mainline inclusion
from mainline-v6.4-rc2
commit 4b3cb1d1
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I8WZXW

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4b3cb1d108bfc2aebb0d7c8a52261a53cf7f5786

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

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: default avatar <syzbot+394aa8a792cb99dbc837@syzkaller.appspotmail.com>
Reported-by: default avatar <syzbot+344aaa8697ebd232bfc8@syzkaller.appspotmail.com>
Link: https://syzkaller.appspot.com/bug?id=db56459ea4ac4a676ae4b4678f633e55da005a9b


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>

Conflicts:
	fs/ext4/hash.c
	fs/ext4/namei.c

Signed-off-by: default avatarBaokun Li <libaokun1@huawei.com>
parent 64a4ccb1
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -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))
+31 −9
Original line number Diff line number Diff line
@@ -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,7 +713,8 @@ static struct stats dx_show_leaf(struct inode *dir,
						name = fname_crypto_str.name;
						len = fname_crypto_str.len;
					}
					ext4fs_dirhash(dir, de->name,
					(void) ext4fs_dirhash(dir,
							de->name,
							de->name_len, &h);
					printk("%*.s:(E)%x.%u ", len, name,
					       h.hash, (unsigned) ((char *) de
@@ -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;