#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master
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+9db318d6167044609878@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=9db318d6167044609878 Fixes: 665575cff098 ("filemap: move prefaulting out of hot write path") Cc: stable@vger.kernel.org Signed-off-by: Deepanshu Kartikey kartikey406@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;