Commit 4b3cb1d1 authored by Theodore Ts'o's avatar Theodore Ts'o

ext4: improve error handling from ext4_dirhash()

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=db56459ea4ac4a676ae4b4678f633e55da005a9bSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent a44be64b
...@@ -277,7 +277,11 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len, ...@@ -277,7 +277,11 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
} }
default: default:
hinfo->hash = 0; hinfo->hash = 0;
return -1; hinfo->minor_hash = 0;
ext4_warning(dir->i_sb,
"invalid/unsupported hash tree version %u",
hinfo->hash_version);
return -EINVAL;
} }
hash = hash & ~1; hash = hash & ~1;
if (hash == (EXT4_HTREE_EOF_32BIT << 1)) if (hash == (EXT4_HTREE_EOF_32BIT << 1))
......
...@@ -674,7 +674,7 @@ static struct stats dx_show_leaf(struct inode *dir, ...@@ -674,7 +674,7 @@ static struct stats dx_show_leaf(struct inode *dir,
len = de->name_len; len = de->name_len;
if (!IS_ENCRYPTED(dir)) { if (!IS_ENCRYPTED(dir)) {
/* Directory is not encrypted */ /* Directory is not encrypted */
ext4fs_dirhash(dir, de->name, (void) ext4fs_dirhash(dir, de->name,
de->name_len, &h); de->name_len, &h);
printk("%*.s:(U)%x.%u ", len, printk("%*.s:(U)%x.%u ", len,
name, h.hash, name, h.hash,
...@@ -709,8 +709,9 @@ static struct stats dx_show_leaf(struct inode *dir, ...@@ -709,8 +709,9 @@ static struct stats dx_show_leaf(struct inode *dir,
if (IS_CASEFOLDED(dir)) if (IS_CASEFOLDED(dir))
h.hash = EXT4_DIRENT_HASH(de); h.hash = EXT4_DIRENT_HASH(de);
else else
ext4fs_dirhash(dir, de->name, (void) ext4fs_dirhash(dir,
de->name_len, &h); de->name,
de->name_len, &h);
printk("%*.s:(E)%x.%u ", len, name, printk("%*.s:(E)%x.%u ", len, name,
h.hash, (unsigned) ((char *) de h.hash, (unsigned) ((char *) de
- base)); - base));
...@@ -720,7 +721,8 @@ static struct stats dx_show_leaf(struct inode *dir, ...@@ -720,7 +721,8 @@ static struct stats dx_show_leaf(struct inode *dir,
#else #else
int len = de->name_len; int len = de->name_len;
char *name = de->name; 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, printk("%*.s:%x.%u ", len, name, h.hash,
(unsigned) ((char *) de - base)); (unsigned) ((char *) de - base));
#endif #endif
...@@ -849,8 +851,14 @@ dx_probe(struct ext4_filename *fname, struct inode *dir, ...@@ -849,8 +851,14 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed; hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
/* hash is already computed for encrypted casefolded directory */ /* hash is already computed for encrypted casefolded directory */
if (fname && fname_name(fname) && if (fname && fname_name(fname) &&
!(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir))) !(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir))) {
ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo); 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; hash = hinfo->hash;
if (root->info.unused_flags & 1) { if (root->info.unused_flags & 1) {
...@@ -1111,7 +1119,12 @@ static int htree_dirblock_to_tree(struct file *dir_file, ...@@ -1111,7 +1119,12 @@ static int htree_dirblock_to_tree(struct file *dir_file,
hinfo->minor_hash = 0; hinfo->minor_hash = 0;
} }
} else { } else {
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) || if ((hinfo->hash < start_hash) ||
((hinfo->hash == start_hash) && ((hinfo->hash == start_hash) &&
...@@ -1313,8 +1326,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, ...@@ -1313,8 +1326,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
if (de->name_len && de->inode) { if (de->name_len && de->inode) {
if (ext4_hash_in_dirent(dir)) if (ext4_hash_in_dirent(dir))
h.hash = EXT4_DIRENT_HASH(de); h.hash = EXT4_DIRENT_HASH(de);
else else {
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--;
map_tail->hash = h.hash; map_tail->hash = h.hash;
map_tail->offs = ((char *) de - base)>>2; map_tail->offs = ((char *) de - base)>>2;
...@@ -1452,10 +1469,9 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname, ...@@ -1452,10 +1469,9 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
hinfo->hash_version = DX_HASH_SIPHASH; hinfo->hash_version = DX_HASH_SIPHASH;
hinfo->seed = NULL; hinfo->seed = NULL;
if (cf_name->name) if (cf_name->name)
ext4fs_dirhash(dir, cf_name->name, cf_name->len, hinfo); return ext4fs_dirhash(dir, cf_name->name, cf_name->len, hinfo);
else else
ext4fs_dirhash(dir, iname->name, iname->len, hinfo); return ext4fs_dirhash(dir, iname->name, iname->len, hinfo);
return 0;
} }
#endif #endif
...@@ -2298,10 +2314,15 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname, ...@@ -2298,10 +2314,15 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed; fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
/* casefolded encrypted hashes are computed on fname setup */ /* casefolded encrypted hashes are computed on fname setup */
if (!ext4_hash_in_dirent(dir)) if (!ext4_hash_in_dirent(dir)) {
ext4fs_dirhash(dir, fname_name(fname), int err = ext4fs_dirhash(dir, fname_name(fname),
fname_len(fname), &fname->hinfo); fname_len(fname), &fname->hinfo);
if (err < 0) {
brelse(bh2);
brelse(bh);
return err;
}
}
memset(frames, 0, sizeof(frames)); memset(frames, 0, sizeof(frames));
frame = frames; frame = frames;
frame->entries = entries; frame->entries = entries;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment