]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ext4: skip split extent recovery on corruption
authorhongao <hongao@uniontech.com>
Tue, 24 Mar 2026 01:58:15 +0000 (09:58 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Sat, 28 Mar 2026 03:38:52 +0000 (23:38 -0400)
ext4_split_extent_at() retries after ext4_ext_insert_extent() fails by
refinding the original extent and restoring its length. That recovery is
only safe for transient resource failures such as -ENOSPC, -EDQUOT, and
-ENOMEM.

When ext4_ext_insert_extent() fails because the extent tree is already
corrupted, ext4_find_extent() can return a leaf path without p_ext.
ext4_split_extent_at() then dereferences path[depth].p_ext while trying to
fix up the original extent length, causing a NULL pointer dereference while
handling a pre-existing filesystem corruption.

Do not enter the recovery path for corruption errors, and validate p_ext
after refinding the extent before touching it. This keeps the recovery path
limited to cases it can actually repair and turns the syzbot-triggered crash
into a proper corruption report.

Fixes: 716b9c23b862 ("ext4: refactor split and convert extents")
Reported-by: syzbot+1ffa5d865557e51cb604@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=1ffa5d865557e51cb604
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: hongao <hongao@uniontech.com>
Link: https://patch.msgid.link/EF77870F23FF9C90+20260324015815.35248-1-hongao@uniontech.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Cc: stable@kernel.org
fs/ext4/extents.c

index da78eb23aaa68620664f2dc9dc0abb787e980fd1..8cce1479be6d1e8d4f57428a0a0ed1088744e72e 100644 (file)
@@ -3254,6 +3254,9 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
 
        insert_err = PTR_ERR(path);
        err = 0;
+       if (insert_err != -ENOSPC && insert_err != -EDQUOT &&
+           insert_err != -ENOMEM)
+               goto out_path;
 
        /*
         * Get a new path to try to zeroout or fix the extent length.
@@ -3270,13 +3273,20 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle,
                goto out_path;
        }
 
+       depth = ext_depth(inode);
+       ex = path[depth].p_ext;
+       if (!ex) {
+               EXT4_ERROR_INODE(inode,
+                                "bad extent address lblock: %lu, depth: %d pblock %llu",
+                                (unsigned long)ee_block, depth, path[depth].p_block);
+               err = -EFSCORRUPTED;
+               goto out;
+       }
+
        err = ext4_ext_get_access(handle, inode, path + depth);
        if (err)
                goto out;
 
-       depth = ext_depth(inode);
-       ex = path[depth].p_ext;
-
 fix_extent_len:
        ex->ee_len = orig_ex.ee_len;
        err = ext4_ext_dirty(handle, inode, path + path->p_depth);