[syzbot] [ext4?] KASAN: use-after-free Read in ext4_ext_insert_extent

2 views
Skip to first unread message

syzbot

unread,
May 25, 2025, 2:12:33 PMMay 25
to adilger...@dilger.ca, linux...@vger.kernel.org, linux-...@vger.kernel.org, syzkall...@googlegroups.com, ty...@mit.edu
Hello,

syzbot found the following issue on:

HEAD commit: d0c22de9995b Merge tag 'input-for-v6.15-rc7' of git://git...
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=15e00df4580000
kernel config: https://syzkaller.appspot.com/x/.config?x=a1de0d8596cea805
dashboard link: https://syzkaller.appspot.com/bug?extid=9db318d6167044609878
compiler: Debian clang version 20.1.6 (++20250514063057+1e4d39e07757-1~exp1~20250514183223.118), Debian LLD 20.1.6
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=16931170580000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=14683ad4580000

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/cf852e5656de/disk-d0c22de9.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/761933abe01b/vmlinux-d0c22de9.xz
kernel image: https://storage.googleapis.com/syzbot-assets/187857154891/bzImage-d0c22de9.xz
mounted in repro: https://storage.googleapis.com/syzbot-assets/992dec4e0af9/mount_0.gz
fsck result: failed (log: https://syzkaller.appspot.com/x/fsck.log?x=151468e8580000)

IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+9db318...@syzkaller.appspotmail.com

==================================================================
BUG: KASAN: slab-use-after-free in ext4_ext_insert_extent+0x41b6/0x4af0 fs/ext4/extents.c:2084
Read of size 4 at addr ffff88802db31c18 by task syz-executor706/7034

CPU: 1 UID: 0 PID: 7034 Comm: syz-executor706 Not tainted 6.15.0-rc7-syzkaller-00152-gd0c22de9995b #0 PREEMPT(full)
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 05/07/2025
Call Trace:
<TASK>
dump_stack_lvl+0x189/0x250 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:408 [inline]
print_report+0xb4/0x290 mm/kasan/report.c:521
kasan_report+0x118/0x150 mm/kasan/report.c:634
ext4_ext_insert_extent+0x41b6/0x4af0 fs/ext4/extents.c:2084
ext4_ext_map_blocks+0x179c/0x67c0 fs/ext4/extents.c:4400
ext4_map_create_blocks fs/ext4/inode.c:520 [inline]
ext4_map_blocks+0x807/0x1740 fs/ext4/inode.c:706
_ext4_get_block+0x200/0x4c0 fs/ext4/inode.c:785
ext4_get_block_unwritten+0x2e/0x100 fs/ext4/inode.c:818
ext4_block_write_begin+0x543/0x1290 fs/ext4/inode.c:1067
ext4_write_begin+0x6f6/0x12c0 fs/ext4/ext4_jbd2.h:-1
ext4_da_write_begin+0x33a/0xa60 fs/ext4/inode.c:2932
generic_perform_write+0x2c4/0x910 mm/filemap.c:4103
ext4_buffered_write_iter+0xce/0x3a0 fs/ext4/file.c:299
ext4_file_write_iter+0x298/0x1bc0 fs/ext4/file.c:-1
new_sync_write fs/read_write.c:591 [inline]
vfs_write+0x54b/0xa90 fs/read_write.c:684
ksys_pwrite64 fs/read_write.c:791 [inline]
__do_sys_pwrite64 fs/read_write.c:799 [inline]
__se_sys_pwrite64 fs/read_write.c:796 [inline]
__x64_sys_pwrite64+0x193/0x220 fs/read_write.c:796
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf6/0x210 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f0078162e09
Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 b1 18 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f0078117218 EFLAGS: 00000246 ORIG_RAX: 0000000000000012
RAX: ffffffffffffffda RBX: 00007f00781ea6c8 RCX: 00007f0078162e09
RDX: 000000000000fdef RSI: 0000200000000140 RDI: 0000000000000004
RBP: 00007f00781ea6c0 R08: 0000000000000000 R09: 0000000000000000
R10: 000000000000fecc R11: 0000000000000246 R12: 00007f00781b7630
R13: 0000200000000000 R14: 00002000000000c0 R15: 00007f00781b706b
</TASK>

Allocated by task 5844:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
unpoison_slab_object mm/kasan/common.c:319 [inline]
__kasan_slab_alloc+0x6c/0x80 mm/kasan/common.c:345
kasan_slab_alloc include/linux/kasan.h:250 [inline]
slab_post_alloc_hook mm/slub.c:4147 [inline]
slab_alloc_node mm/slub.c:4196 [inline]
kmem_cache_alloc_noprof+0x1c1/0x3c0 mm/slub.c:4203
getname_flags+0xb8/0x540 fs/namei.c:146
getname include/linux/fs.h:2852 [inline]
__do_sys_symlink fs/namei.c:4770 [inline]
__se_sys_symlink fs/namei.c:4768 [inline]
__x64_sys_symlink+0x6a/0x90 fs/namei.c:4768
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf6/0x210 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

Freed by task 5844:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x3e/0x80 mm/kasan/common.c:68
kasan_save_free_info+0x46/0x50 mm/kasan/generic.c:576
poison_slab_object mm/kasan/common.c:247 [inline]
__kasan_slab_free+0x62/0x70 mm/kasan/common.c:264
kasan_slab_free include/linux/kasan.h:233 [inline]
slab_free_hook mm/slub.c:2380 [inline]
slab_free mm/slub.c:4642 [inline]
kmem_cache_free+0x192/0x3f0 mm/slub.c:4744
do_symlinkat+0x39f/0x3f0 fs/namei.c:4757
__do_sys_symlink fs/namei.c:4770 [inline]
__se_sys_symlink fs/namei.c:4768 [inline]
__x64_sys_symlink+0x7a/0x90 fs/namei.c:4768
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf6/0x210 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

The buggy address belongs to the object at ffff88802db31100
which belongs to the cache names_cache of size 4096
The buggy address is located 2840 bytes inside of
freed 4096-byte region [ffff88802db31100, ffff88802db32100)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x2db30
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
anon flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff)
page_type: f5(slab)
raw: 00fff00000000040 ffff88801b2b8780 0000000000000000 dead000000000001
raw: 0000000000000000 0000000000070007 00000000f5000000 0000000000000000
head: 00fff00000000040 ffff88801b2b8780 0000000000000000 dead000000000001
head: 0000000000000000 0000000000070007 00000000f5000000 0000000000000000
head: 00fff00000000003 ffffea0000b6cc01 00000000ffffffff 00000000ffffffff
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5844, tgid 5844 (udevd), ts 111781614645, free_ts 111672478666
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1d8/0x230 mm/page_alloc.c:1714
prep_new_page mm/page_alloc.c:1722 [inline]
get_page_from_freelist+0x21c7/0x22a0 mm/page_alloc.c:3684
__alloc_frozen_pages_noprof+0x181/0x370 mm/page_alloc.c:4966
alloc_pages_mpol+0x232/0x4a0 mm/mempolicy.c:2301
alloc_slab_page mm/slub.c:2450 [inline]
allocate_slab+0x8a/0x3b0 mm/slub.c:2618
new_slab mm/slub.c:2672 [inline]
___slab_alloc+0xbfc/0x1480 mm/slub.c:3858
__slab_alloc mm/slub.c:3948 [inline]
__slab_alloc_node mm/slub.c:4023 [inline]
slab_alloc_node mm/slub.c:4184 [inline]
kmem_cache_alloc_noprof+0x283/0x3c0 mm/slub.c:4203
getname_flags+0xb8/0x540 fs/namei.c:146
getname include/linux/fs.h:2852 [inline]
__do_sys_symlink fs/namei.c:4770 [inline]
__se_sys_symlink fs/namei.c:4768 [inline]
__x64_sys_symlink+0x5d/0x90 fs/namei.c:4768
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf6/0x210 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
page last free pid 5189 tgid 5189 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
free_pages_prepare mm/page_alloc.c:1258 [inline]
__free_frozen_pages+0xb05/0xcd0 mm/page_alloc.c:2721
discard_slab mm/slub.c:2716 [inline]
__put_partials+0x161/0x1c0 mm/slub.c:3185
put_cpu_partial+0x17c/0x250 mm/slub.c:3260
__slab_free+0x2f7/0x400 mm/slub.c:4512
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x9a/0x140 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x148/0x160 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x22/0x80 mm/kasan/common.c:329
kasan_slab_alloc include/linux/kasan.h:250 [inline]
slab_post_alloc_hook mm/slub.c:4147 [inline]
slab_alloc_node mm/slub.c:4196 [inline]
__do_kmalloc_node mm/slub.c:4326 [inline]
__kmalloc_noprof+0x224/0x4f0 mm/slub.c:4339
kmalloc_noprof include/linux/slab.h:909 [inline]
tomoyo_realpath_from_path+0xe3/0x5d0 security/tomoyo/realpath.c:251
tomoyo_get_realpath security/tomoyo/file.c:151 [inline]
tomoyo_path_perm+0x213/0x4b0 security/tomoyo/file.c:822
security_inode_getattr+0x12f/0x330 security/security.c:2377
vfs_getattr fs/stat.c:256 [inline]
vfs_fstat fs/stat.c:278 [inline]
__do_sys_newfstat fs/stat.c:546 [inline]
__se_sys_newfstat fs/stat.c:543 [inline]
__x64_sys_newfstat+0xfe/0x200 fs/stat.c:543
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xf6/0x210 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f

Memory state around the buggy address:
ffff88802db31b00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88802db31b80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>ffff88802db31c00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff88802db31c80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff88802db31d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================


---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzk...@googlegroups.com.

syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.

If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title

If you want syzbot to run the reproducer, reply with:
#syz test: git://repo/address.git branch-or-commit-hash
If you attach or paste a git patch, syzbot will apply it before testing.

If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)

If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report

If you want to undo deduplication, reply with:
#syz undup

syzbot

unread,
May 26, 2025, 10:53:05 AMMay 26
to adilger...@dilger.ca, ak...@linux-foundation.org, dave....@linux.intel.com, linux...@vger.kernel.org, linux-...@vger.kernel.org, syzkall...@googlegroups.com, ty...@mit.edu
syzbot has bisected this issue to:

commit 665575cff098b696995ddaddf4646a4099941f5e
Author: Dave Hansen <dave....@linux.intel.com>
Date: Fri Feb 28 20:37:22 2025 +0000

filemap: move prefaulting out of hot write path

bisection log: https://syzkaller.appspot.com/x/bisect.txt?x=14435170580000
start commit: d0c22de9995b Merge tag 'input-for-v6.15-rc7' of git://git...
git tree: upstream
final oops: https://syzkaller.appspot.com/x/report.txt?x=16435170580000
console output: https://syzkaller.appspot.com/x/log.txt?x=12435170580000
Reported-by: syzbot+9db318...@syzkaller.appspotmail.com
Fixes: 665575cff098 ("filemap: move prefaulting out of hot write path")

For information about bisection process see: https://goo.gl/tpsmEJ#bisection

syzbot

unread,
Sep 30, 2025, 11:20:33 AM (yesterday) Sep 30
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] ext4: fix use-after-free in ext4_ext_insert_extent()
Author: karti...@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

syzbot reported a use-after-free bug in ext4_ext_insert_extent() where
fex->ee_block is accessed after the extent header has been freed. This
occurs when the extent header is corrupted or freed by a concurrent
thread during a write operation.

The issue is triggered when multiple threads perform concurrent writes
to the same file. After commit 665575cff098 ("filemap: move prefaulting
out of hot write path"), the write path no longer prefaults pages,
creating a race window where:

1. Thread A enters ext4_ext_insert_extent() and gets extent header pointer
2. Thread B modifies the extent tree, potentially freeing the header
3. Thread A dereferences fex->ee_block from the freed header, causing UAF

Fix this by validating the extent header's magic number and ensuring it
has valid entries before dereferencing the last extent pointer.

Reported-by: syzbot+9db318...@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9db318d6167044609878
Fixes: 665575cff098 ("filemap: move prefaulting out of hot write path")
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/ext4/extents.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ca5499e9412b..d71b0fff41cc 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2083,6 +2083,12 @@ ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
/* probably next leaf has space for us? */
fex = EXT_LAST_EXTENT(eh);
next = EXT_MAX_BLOCKS;
+ if (le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC ||
+ le16_to_cpu(eh->eh_entries) == 0) {
+ EXT4_ERROR_INODE(inode, "corrupted extent header");
+ err = -EFSCORRUPTED;
+ goto errout;
+ }
if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block))
next = ext4_ext_next_leaf_block(path);
if (next != EXT_MAX_BLOCKS) {
--
2.43.0

syzbot

unread,
Sep 30, 2025, 4:57:27 PM (22 hours ago) Sep 30
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] ext4: fix use-after-free in extent header access
syzbot reported multiple use-after-free bugs when accessing extent headers
in various ext4 functions. These occur because extent headers can be freed
by concurrent operations while other threads still hold pointers to them.

The issue is triggered by racing threads performing concurrent writes to
the same file. After commit 665575cff098 ("filemap: move prefaulting out
of hot write path"), the write path no longer prefaults pages in the hot
path, creating a wider race window where:

1. Thread A calls ext4_find_extent() and gets a path with extent headers
2. Thread A's write attempt fails, entering the slow path
3. During the gap, Thread B modifies the extent tree, freeing nodes
4. Thread A continues using the now-freed extent headers, causing UAF

Fix this by validating the extent header in ext4_find_extent() before
returning the path. This ensures all callers receive a valid extent path,
fixing the race at a single point rather than adding checks throughout
the codebase.

This addresses crashes in ext4_ext_insert_extent(), ext4_ext_binsearch(),
and potentially other locations that use extent paths.

Reported-by: syzbot+9db318...@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9db318d6167044609878
Fixes: 665575cff098 ("filemap: move prefaulting out of hot write path")
Cc: sta...@vger.kernel.org
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/ext4/extents.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ca5499e9412b..04ceae5b0a34 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4200,6 +4200,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
unsigned int allocated_clusters = 0;
struct ext4_allocation_request ar;
ext4_lblk_t cluster_offset;
+ struct ext4_extent_header *eh;

ext_debug(inode, "blocks %u/%u requested\n", map->m_lblk, map->m_len);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
@@ -4212,7 +4213,12 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
}

depth = ext_depth(inode);
-
+ eh = path[depth].p_hdr;
+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC) {
+ EXT4_ERROR_INODE(inode, "invalid extent header after find_extent");
+ err = -EFSCORRUPTED;
+ goto out;
+ }
/*
* consistent leaf must not be empty;
* this situation is possible, though, _during_ tree modification;
--
2.43.0

syzbot

unread,
Sep 30, 2025, 6:05:15 PM (20 hours ago) Sep 30
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] ext4: fix use-after-free in extent header access
Author: karti...@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

syzbot reported use-after-free bugs when accessing extent headers in
ext4_ext_insert_extent() and ext4_ext_correct_indexes(). These occur
when the extent path structure becomes invalid during operations.

The crashes show two patterns:
1. In ext4_ext_map_blocks(), the extent header can be corrupted after
ext4_find_extent() returns, particularly during concurrent writes
to the same file.
2. In ext4_ext_correct_indexes(), accessing path[depth] causes a
use-after-free, indicating the path structure itself is corrupted.

This is partially exposed by commit 665575cff098 ("filemap: move
prefaulting out of hot write path") which changed timing windows in
the write path, making these races more likely to occur.

Fix this by adding validation checks:
- In ext4_ext_map_blocks(): validate the extent header after getting
the path from ext4_find_extent()
- In ext4_ext_correct_indexes(): validate the path pointer before
dereferencing and check extent header magic

While these checks are defensive and don't address the root cause of
path corruption, they prevent kernel crashes from invalid memory access.
A more comprehensive fix to path lifetime management may be needed in
the future.

Reported-by: syzbot+9db318...@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=9db318d6167044609878
Fixes: 665575cff098 ("filemap: move prefaulting out of hot write path")
Cc: sta...@vger.kernel.org
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/ext4/extents.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ca5499e9412b..903578d5f68d 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1708,7 +1708,9 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
struct ext4_extent *ex;
__le32 border;
int k, err = 0;
-
+ if (!path || depth < 0 || depth > EXT4_MAX_EXTENT_DEPTH) {
+ return -EFSCORRUPTED;
+ }
eh = path[depth].p_hdr;
ex = path[depth].p_ext;

@@ -4200,6 +4202,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
unsigned int allocated_clusters = 0;
struct ext4_allocation_request ar;
ext4_lblk_t cluster_offset;
+ struct ext4_extent_header *eh;

ext_debug(inode, "blocks %u/%u requested\n", map->m_lblk, map->m_len);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
@@ -4212,7 +4215,12 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,

syzbot

unread,
Sep 30, 2025, 6:49:41 PM (20 hours ago) Sep 30
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] ext4: add defensive checks for extent header corruption
Author: karti...@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

syzbot reported use-after-free bugs when accessing extent headers in
multiple locations. Testing revealed crashes in both ext4_ext_map_blocks()
and ext4_ext_correct_indexes() where extent headers or paths contain
invalid data.

The crashes occur when:
1. In ext4_ext_map_blocks(): After ext4_find_extent() returns, the extent
header at path[depth] can be corrupted
2. In ext4_ext_correct_indexes(): The path structure itself may be invalid
when accessed at path[depth]

These issues are more easily triggered after commit 665575cff098 ("filemap:
move prefaulting out of hot write path") which changed timing in write
paths, though the underlying vulnerability appears to be pre-existing.

Add defensive validation checks:
- In ext4_ext_map_blocks(): Validate extent header magic after getting path
- In ext4_ext_correct_indexes(): Check path validity before dereferencing

These checks prevent crashes but don't address the root cause of how
these structures become corrupted. Further investigation is needed for
a complete fix.
Signed-off-by: Deepanshu Kartikey <karti...@gmail.com>
---
fs/ext4/extents.c | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ca5499e9412b..04d2328ee1d4 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1708,7 +1708,8 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
struct ext4_extent *ex;
__le32 border;
int k, err = 0;
-
+ if (!path || depth < 0 || depth > EXT4_MAX_EXTENT_DEPTH)
+ return -EFSCORRUPTED;
eh = path[depth].p_hdr;
ex = path[depth].p_ext;

@@ -4200,19 +4201,26 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
unsigned int allocated_clusters = 0;
struct ext4_allocation_request ar;
ext4_lblk_t cluster_offset;
+ struct ext4_extent_header *eh;

ext_debug(inode, "blocks %u/%u requested\n", map->m_lblk, map->m_len);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
-
+ depth = ext_depth(inode);
+ if (depth == 0)
+ eh = ext_inode_hdr(inode);
+ else
+ eh = path[depth].p_hdr;
+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC) {
+ EXT4_ERROR_INODE(inode, "invalid extent header at depth %d", depth);
+ err = -EFSCORRUPTED;
+ goto out;
+ }
/* find extent for this block */
path = ext4_find_extent(inode, map->m_lblk, NULL, flags);
if (IS_ERR(path)) {
err = PTR_ERR(path);
goto out;
}
-
- depth = ext_depth(inode);
-

syzbot

unread,
Sep 30, 2025, 7:09:06 PM (19 hours ago) Sep 30
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
For archival purposes, forwarding an incoming command email to
linux-...@vger.kernel.org, syzkall...@googlegroups.com.

***

Subject: [PATCH] ext4: add validation checks for corrupted extent headers
syzbot reported use-after-free bugs in extent tree operations, particularly
in ext4_ext_binsearch_idx() and ext4_ext_binsearch() called from
ext4_find_extent(), as well as crashes in ext4_ext_correct_indexes() and
ext4_ext_map_blocks().

The crashes occur when extent headers contain invalid data (wrong magic
number or freed memory). This can happen during concurrent operations on
the same inode where extent tree modifications lead to stale pointers.

Add validation checks at key points:
1. In ext4_find_extent(): validate extent headers before calling
ext4_ext_binsearch_idx() and ext4_ext_binsearch()
2. In ext4_ext_correct_indexes(): validate path and depth before
dereferencing path[depth]
3. In ext4_ext_map_blocks(): validate extent header after getting
the path from ext4_find_extent()

These defensive checks prevent crashes from invalid extent headers,
though the root cause of the corruption needs further investigation.
The checks ensure the code fails gracefully with -EFSCORRUPTED rather
than crashing on invalid memory access.
fs/ext4/extents.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index ca5499e9412b..ef3870afb8fb 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -930,7 +930,11 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
while (i) {
ext_debug(inode, "depth %d: num %d, max %d\n",
ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
-
+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC) {
+ EXT4_ERROR_INODE(inode, "invalid extent header before binsearch_idx");
+ ret = -EFSCORRUPTED;
+ goto err;
+ }
ext4_ext_binsearch_idx(inode, path + ppos, block);
path[ppos].p_block = ext4_idx_pblock(path[ppos].p_idx);
path[ppos].p_depth = i;
@@ -952,12 +956,17 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
path[ppos].p_ext = NULL;
path[ppos].p_idx = NULL;

+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC) {
+ EXT4_ERROR_INODE(inode, "invalid extent header before binsearch");
+ ret = -EFSCORRUPTED;
+ goto err;
+ }
/* find extent */
ext4_ext_binsearch(inode, path + ppos, block);
/* if not an empty leaf */
if (path[ppos].p_ext)
path[ppos].p_block = ext4_ext_pblock(path[ppos].p_ext);
-
+
ext4_ext_show_path(inode, path);

return path;
@@ -1708,7 +1717,8 @@ static int ext4_ext_correct_indexes(handle_t *handle, struct inode *inode,
struct ext4_extent *ex;
__le32 border;
int k, err = 0;
-
+ if (!path || depth < 0 || depth > EXT4_MAX_EXTENT_DEPTH)
+ return -EFSCORRUPTED;
eh = path[depth].p_hdr;
ex = path[depth].p_ext;

@@ -4200,6 +4210,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
unsigned int allocated_clusters = 0;
struct ext4_allocation_request ar;
ext4_lblk_t cluster_offset;
+ struct ext4_extent_header *eh;

ext_debug(inode, "blocks %u/%u requested\n", map->m_lblk, map->m_len);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
@@ -4212,7 +4223,12 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
}

depth = ext_depth(inode);
-
+ eh = path[depth].p_hdr;
+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC) {
+ EXT4_ERROR_INODE(inode, "invalid extent header at depth %d", depth);
+ err = -EFSCORRUPTED;
+ goto out;
+ }
/*

syzbot

unread,
Sep 30, 2025, 8:10:27 PM (18 hours ago) Sep 30
to linux-...@vger.kernel.org, syzkall...@googlegroups.com
index ca5499e9412b..fef9db80d65c 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -930,7 +930,11 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
while (i) {
ext_debug(inode, "depth %d: num %d, max %d\n",
ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max));
-
+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC || le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)) {
+ EXT4_ERROR_INODE(inode, "invalid extent header before binsearch_idx");
+ ret = -EFSCORRUPTED;
+ goto err;
+ }
ext4_ext_binsearch_idx(inode, path + ppos, block);
path[ppos].p_block = ext4_idx_pblock(path[ppos].p_idx);
path[ppos].p_depth = i;
@@ -952,12 +956,17 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
path[ppos].p_ext = NULL;
path[ppos].p_idx = NULL;

+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC || le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)) {
+ if (!eh || le16_to_cpu(eh->eh_magic) != EXT4_EXT_MAGIC || le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)) {
Reply all
Reply to author
Forward
0 new messages