Skip to content

Commit 4b3cb1d

Browse files
committed
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: [email protected] Link: https://lore.kernel.org/r/[email protected] Reported-by: [email protected] Reported-by: [email protected] Link: https://syzkaller.appspot.com/bug?id=db56459ea4ac4a676ae4b4678f633e55da005a9b Signed-off-by: Theodore Ts'o <[email protected]>
1 parent a44be64 commit 4b3cb1d

File tree

2 files changed

+42
-17
lines changed

2 files changed

+42
-17
lines changed

fs/ext4/hash.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,11 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
277277
}
278278
default:
279279
hinfo->hash = 0;
280-
return -1;
280+
hinfo->minor_hash = 0;
281+
ext4_warning(dir->i_sb,
282+
"invalid/unsupported hash tree version %u",
283+
hinfo->hash_version);
284+
return -EINVAL;
281285
}
282286
hash = hash & ~1;
283287
if (hash == (EXT4_HTREE_EOF_32BIT << 1))

fs/ext4/namei.c

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ static struct stats dx_show_leaf(struct inode *dir,
674674
len = de->name_len;
675675
if (!IS_ENCRYPTED(dir)) {
676676
/* Directory is not encrypted */
677-
ext4fs_dirhash(dir, de->name,
677+
(void) ext4fs_dirhash(dir, de->name,
678678
de->name_len, &h);
679679
printk("%*.s:(U)%x.%u ", len,
680680
name, h.hash,
@@ -709,8 +709,9 @@ static struct stats dx_show_leaf(struct inode *dir,
709709
if (IS_CASEFOLDED(dir))
710710
h.hash = EXT4_DIRENT_HASH(de);
711711
else
712-
ext4fs_dirhash(dir, de->name,
713-
de->name_len, &h);
712+
(void) ext4fs_dirhash(dir,
713+
de->name,
714+
de->name_len, &h);
714715
printk("%*.s:(E)%x.%u ", len, name,
715716
h.hash, (unsigned) ((char *) de
716717
- base));
@@ -720,7 +721,8 @@ static struct stats dx_show_leaf(struct inode *dir,
720721
#else
721722
int len = de->name_len;
722723
char *name = de->name;
723-
ext4fs_dirhash(dir, de->name, de->name_len, &h);
724+
(void) ext4fs_dirhash(dir, de->name,
725+
de->name_len, &h);
724726
printk("%*.s:%x.%u ", len, name, h.hash,
725727
(unsigned) ((char *) de - base));
726728
#endif
@@ -849,8 +851,14 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
849851
hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
850852
/* hash is already computed for encrypted casefolded directory */
851853
if (fname && fname_name(fname) &&
852-
!(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)))
853-
ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo);
854+
!(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir))) {
855+
int ret = ext4fs_dirhash(dir, fname_name(fname),
856+
fname_len(fname), hinfo);
857+
if (ret < 0) {
858+
ret_err = ERR_PTR(ret);
859+
goto fail;
860+
}
861+
}
854862
hash = hinfo->hash;
855863

856864
if (root->info.unused_flags & 1) {
@@ -1111,7 +1119,12 @@ static int htree_dirblock_to_tree(struct file *dir_file,
11111119
hinfo->minor_hash = 0;
11121120
}
11131121
} else {
1114-
ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
1122+
err = ext4fs_dirhash(dir, de->name,
1123+
de->name_len, hinfo);
1124+
if (err < 0) {
1125+
count = err;
1126+
goto errout;
1127+
}
11151128
}
11161129
if ((hinfo->hash < start_hash) ||
11171130
((hinfo->hash == start_hash) &&
@@ -1313,8 +1326,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
13131326
if (de->name_len && de->inode) {
13141327
if (ext4_hash_in_dirent(dir))
13151328
h.hash = EXT4_DIRENT_HASH(de);
1316-
else
1317-
ext4fs_dirhash(dir, de->name, de->name_len, &h);
1329+
else {
1330+
int err = ext4fs_dirhash(dir, de->name,
1331+
de->name_len, &h);
1332+
if (err < 0)
1333+
return err;
1334+
}
13181335
map_tail--;
13191336
map_tail->hash = h.hash;
13201337
map_tail->offs = ((char *) de - base)>>2;
@@ -1452,10 +1469,9 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
14521469
hinfo->hash_version = DX_HASH_SIPHASH;
14531470
hinfo->seed = NULL;
14541471
if (cf_name->name)
1455-
ext4fs_dirhash(dir, cf_name->name, cf_name->len, hinfo);
1472+
return ext4fs_dirhash(dir, cf_name->name, cf_name->len, hinfo);
14561473
else
1457-
ext4fs_dirhash(dir, iname->name, iname->len, hinfo);
1458-
return 0;
1474+
return ext4fs_dirhash(dir, iname->name, iname->len, hinfo);
14591475
}
14601476
#endif
14611477

@@ -2298,10 +2314,15 @@ static int make_indexed_dir(handle_t *handle, struct ext4_filename *fname,
22982314
fname->hinfo.seed = EXT4_SB(dir->i_sb)->s_hash_seed;
22992315

23002316
/* casefolded encrypted hashes are computed on fname setup */
2301-
if (!ext4_hash_in_dirent(dir))
2302-
ext4fs_dirhash(dir, fname_name(fname),
2303-
fname_len(fname), &fname->hinfo);
2304-
2317+
if (!ext4_hash_in_dirent(dir)) {
2318+
int err = ext4fs_dirhash(dir, fname_name(fname),
2319+
fname_len(fname), &fname->hinfo);
2320+
if (err < 0) {
2321+
brelse(bh2);
2322+
brelse(bh);
2323+
return err;
2324+
}
2325+
}
23052326
memset(frames, 0, sizeof(frames));
23062327
frame = frames;
23072328
frame->entries = entries;

0 commit comments

Comments
 (0)