]> git.itanic.dy.fi Git - linux-stable/commitdiff
ext4: improve error handling from ext4_dirhash()
authorTheodore Ts'o <tytso@mit.edu>
Sat, 6 May 2023 15:59:13 +0000 (11:59 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 May 2023 09:50:30 +0000 (11:50 +0200)
commit 4b3cb1d108bfc2aebb0d7c8a52261a53cf7f5786 upstream.

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=db56459ea4ac4a676ae4b4678f633e55da005a9b
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/hash.c
fs/ext4/namei.c

index f34f4176c1e7c24e921ea9b0dde96e422e3edfe4..3b03e6b061db1f42499c17395ca239c094f4285f 100644 (file)
@@ -277,7 +277,11 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len,
        }
        default:
                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;
        if (hash == (EXT4_HTREE_EOF_32BIT << 1))
index 0c47e1e3cba435383b8a950ac1e91ae80fc112c9..b87269bae4fbc9a1aa8034901c211ec5ae6e9ee6 100644 (file)
@@ -674,7 +674,7 @@ static struct stats dx_show_leaf(struct inode *dir,
                                len = de->name_len;
                                if (!IS_ENCRYPTED(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,
@@ -709,8 +709,9 @@ static struct stats dx_show_leaf(struct inode *dir,
                                        if (IS_CASEFOLDED(dir))
                                                h.hash = EXT4_DIRENT_HASH(de);
                                        else
-                                               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));
@@ -720,7 +721,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
@@ -849,8 +851,14 @@ dx_probe(struct ext4_filename *fname, struct inode *dir,
        hinfo->seed = EXT4_SB(dir->i_sb)->s_hash_seed;
        /* hash is already computed for encrypted casefolded directory */
        if (fname && fname_name(fname) &&
-                               !(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir)))
-               ext4fs_dirhash(dir, fname_name(fname), fname_len(fname), hinfo);
+           !(IS_ENCRYPTED(dir) && IS_CASEFOLDED(dir))) {
+               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) {
@@ -1111,7 +1119,12 @@ static int htree_dirblock_to_tree(struct file *dir_file,
                                hinfo->minor_hash = 0;
                        }
                } 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) ||
                    ((hinfo->hash == start_hash) &&
@@ -1313,8 +1326,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh,
                if (de->name_len && de->inode) {
                        if (ext4_hash_in_dirent(dir))
                                h.hash = EXT4_DIRENT_HASH(de);
-                       else
-                               ext4fs_dirhash(dir, de->name, de->name_len, &h);
+                       else {
+                               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;
@@ -1452,10 +1469,9 @@ int ext4_fname_setup_ci_filename(struct inode *dir, const struct qstr *iname,
        hinfo->hash_version = DX_HASH_SIPHASH;
        hinfo->seed = NULL;
        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
-               ext4fs_dirhash(dir, iname->name, iname->len, hinfo);
-       return 0;
+               return ext4fs_dirhash(dir, iname->name, iname->len, hinfo);
 }
 #endif
 
@@ -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;
 
        /* casefolded encrypted hashes are computed on fname setup */
-       if (!ext4_hash_in_dirent(dir))
-               ext4fs_dirhash(dir, fname_name(fname),
-                               fname_len(fname), &fname->hinfo);
-
+       if (!ext4_hash_in_dirent(dir)) {
+               int err = ext4fs_dirhash(dir, fname_name(fname),
+                                        fname_len(fname), &fname->hinfo);
+               if (err < 0) {
+                       brelse(bh2);
+                       brelse(bh);
+                       return err;
+               }
+       }
        memset(frames, 0, sizeof(frames));
        frame = frames;
        frame->entries = entries;