History

ext4: skip split extent recovery on corruption

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.

hongao committed 7 months ago

Commit 3ceda171 parent ec0a7501 file · +13 −3Browse files

Unified · Split

fs/ext4/extents.c+13−3
@@ -3254,6 +3254,9 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, · 3253 unmodified lines
32543254
32553255 insert_err = PTR_ERR(path);
32563256 err = 0;
3257+ if (insert_err != -ENOSPC && insert_err != -EDQUOT &&
3258+ insert_err != -ENOMEM)
3259+ goto out_path;
32573260
32583261 /*
32593262 * 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, · 10 unmodified lines
32703273 goto out_path;
32713274 }
32723275
3276+ depth = ext_depth(inode);
3277+ ex = path[depth].p_ext;
3278+ if (!ex) {
3279+ EXT4_ERROR_INODE(inode,
3280+ "bad extent address lblock: %lu, depth: %d pblock %llu",
3281+ (unsigned long)ee_block, depth, path[depth].p_block);
3282+ err = -EFSCORRUPTED;
3283+ goto out;
3284+ }
3285+
32733286 err = ext4_ext_get_access(handle, inode, path + depth);
32743287 if (err)
32753288 goto out;
32763289
3277− depth = ext_depth(inode);
3278− ex = path[depth].p_ext;
3279−
32803290 fix_extent_len:
32813291 ex->ee_len = orig_ex.ee_len;
32823292 err = ext4_ext_dirty(handle, inode, path + path->p_depth);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t