[<prev] [next>] [day] [month] [year] [list]
Message-ID: <2edd9a0c.3e90f.19b0314cfc8.Coremail.3230100410@zju.edu.cn>
Date: Tue, 9 Dec 2025 20:27:34 +0800 (GMT+08:00)
From: 余昊铖 <3230100410@....edu.cn>
To: security@...nel.org, linux-ext4@...r.kernel.org,
linux-kernel@...r.kernel.org
Subject: [PATCH] ext4: Fix KASAN use-after-free in ext4_find_extent
Hello,
I would like to report a potential security issue in the Linux kernel ext4 filesystem, which I found using a modified syzkaller-based kernel fuzzing tool that I developed.
Summary
-------
A local unprivileged user can trigger a use-after-free vulnerability in `ext4_find_extent()` by mounting a crafted ext4 filesystem image and performing file operations.
The vulnerability is caused by missing validation of the inode's root extent header. `ext4_find_extent()` proceeds to use the root extent header without verifying its consistency. A corrupted root extent header can lead to out-of-bounds memory access or the creation of a corrupted `path` array containing invalid pointers. When `ext4_ext_correct_indexes()` (or other functions consuming the path) is subsequently called, it dereferences these invalid pointers, leading to a Use-After-Free (UAF).
By the way, the same reproducer can also triggers slab-out-of-bounds and slab-use-after-free problem in ext4_ext_correct_indexes(). But I haven't figured out why and how to patch it after a week's effort.
I have extracted the crafted ext4 image. And the image passes e2fsck checks without reporting any error:
e2fsck 1.45.5 (07-Jan-2020)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
./image.img: 11/16 files (0.0% non-contiguous), 21/64 blocks
I verified this on Linux kernel version 6.12.51.
Environment
-----------
- Kernel version: 6.12.51 (the complete config is attached)
- Architecture: x86_64
- Hypervisor: QEMU (Standard PC i440FX + PIIX, BIOS 1.13.0-1ubuntu1.1)
- Filesystem: ext4, mounted from a crafted disk image via /dev/loopN
Symptoms and logs
-----------------
When running the syzkaller reproducer, the kernel crashes with a KASAN use-after-free report.
Relevant part of the KASAN report:
[ 21.827456] BUG: KASAN: use-after-free in ext4_find_extent+0x90f/0x9f0
[ 21.828241] EXT4-fs error (device loop2): ext4_map_blocks:707: inode #12: block 335007449088: comm kworker/u9)
[ 21.828301] Read of size 4 at addr ffff88801300a800 by task kworker/u10:3/1232
[ 21.829248]
[ 21.829329] CPU: 1 UID: 0 PID: 1232 Comm: kworker/u10:3 Not tainted 6.12.51 #38
[ 21.829336] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
[ 21.829341] Workqueue: writeback wb_workfn (flush-7:7)
[ 21.829349] Call Trace:
[ 21.829351] <TASK>
[ 21.829354] dump_stack_lvl+0x7d/0xa0
[ 21.829368] print_report+0xcf/0x610
[ 21.829376] ? __virt_addr_valid+0xcb/0x320
[ 21.829385] ? ext4_find_extent+0x90f/0x9f0
[ 21.829392] kasan_report+0xb5/0xe0
[ 21.829400] ? ext4_find_extent+0x90f/0x9f0
[ 21.829407] ext4_find_extent+0x90f/0x9f0
[ 21.829415] ext4_ext_map_blocks+0x13d/0x35b0
[ 21.829423] ? ext4_alloc_io_end_vec+0x25/0x160
[ 21.829432] ? ext4_do_writepages+0x12ea/0x2bd0
[ 21.829439] ? ext4_writepages+0x274/0x500
[ 21.829445] ? __pfx_ext4_ext_map_blocks+0x10/0x10
[ 21.829453] ? percpu_counter_add_batch+0x106/0x250
[ 21.829464] ? __pfx_percpu_counter_add_batch+0x10/0x10
[ 21.829474] ? down_write+0xb3/0x130
[ 21.829483] ? __pfx_down_write+0x10/0x10
[ 21.829490] ? _raw_read_unlock+0x12/0x40
[ 21.829499] ? ext4_es_lookup_extent+0xb1/0x9a0
[ 21.829508] ext4_map_blocks+0x36a/0x10f0
[ 21.829518] ? __pfx_ext4_map_blocks+0x10/0x10
[ 21.829527] ? kasan_save_track+0x14/0x30
[ 21.829534] ? __kasan_slab_alloc+0x59/0x70
[ 21.829542] ? kmem_cache_alloc_noprof+0xe2/0x230
[ 21.829548] ? ext4_journal_check_start+0x165/0x270
[ 21.829554] ext4_do_writepages+0x1690/0x2bd0
[ 21.829561] ? folio_end_writeback+0x150/0x1e0
[ 21.829569] ? __pfx_ext4_do_writepages+0x10/0x10
[ 21.829575] ? writeback_iter+0x6bd/0xd40
[ 21.829584] ? __pfx_blkdev_get_block+0x10/0x10
[ 21.829591] ? __pfx_block_write_full_folio+0x10/0x10
[ 21.829600] ? write_cache_pages+0xc6/0x110
[ 21.829609] ? ext4_writepages+0x274/0x500
[ 21.829615] ext4_writepages+0x274/0x500
[ 21.829621] ? __pfx_ext4_writepages+0x10/0x10
[ 21.829627] ? blk_finish_plug+0x16/0xa0
[ 21.829638] ? __pfx__raw_spin_lock+0x10/0x10
[ 21.829643] ? __pfx__raw_spin_lock_irqsave+0x10/0x10
[ 21.829653] ? __pfx_ext4_writepages+0x10/0x10
[ 21.829659] do_writepages+0x182/0x760
[ 21.829669] ? __pfx_do_writepages+0x10/0x10
[ 21.829678] ? _raw_spin_lock+0x80/0xe0
[ 21.829684] ? __pfx__raw_spin_lock+0x10/0x10
[ 21.829690] __writeback_single_inode+0xb4/0x910
[ 21.829700] writeback_sb_inodes+0x561/0xc50
[ 21.829710] ? fprop_reflect_period_percpu.isra.0+0x1c/0x2a0
[ 21.829718] ? __pfx_writeback_sb_inodes+0x10/0x10
[ 21.829730] ? __pfx_down_read_trylock+0x10/0x10
[ 21.829736] ? wb_over_bg_thresh+0xbe/0x200
[ 21.829744] ? __pfx_move_expired_inodes+0x10/0x10
[ 21.829752] __writeback_inodes_wb+0xbc/0x230
[ 21.829761] wb_writeback+0x4ff/0x760
[ 21.829771] ? wb_over_bg_thresh+0xbe/0x200
[ 21.829779] ? __pfx_wb_writeback+0x10/0x10
[ 21.829789] ? get_nr_dirty_inodes+0xf7/0x180
[ 21.829797] wb_workfn+0x586/0x990
[ 21.829803] ? __pfx_wb_workfn+0x10/0x10
[ 21.829808] ? kick_pool+0x1b4/0x5a0
[ 21.829814] process_scheduled_works+0x923/0x10e0
[ 21.829824] worker_thread+0x434/0xa10
[ 21.829833] ? __kthread_parkme+0xe3/0x160
[ 21.829843] ? __pfx_worker_thread+0x10/0x10
[ 21.829852] kthread+0x2c7/0x3c0
[ 21.829857] ? __pfx_kthread+0x10/0x10
[ 21.829862] ret_from_fork+0x48/0x80
[ 21.829870] ? __pfx_kthread+0x10/0x10
[ 21.829875] ret_from_fork_asm+0x1a/0x30
[ 21.829884] </TASK>
The KASAN report about the bug in ext4_ext_correct_indexes:
[ 19.129192] BUG: KASAN: slab-out-of-bounds in ext4_ext_correct_indexes+0x41b/0x490
[ 19.129725] Read of size 8 at addr ffff888008a4a2a0 by task repro/251
[ 19.130106] EXT4-fs error (device loop1): ext4_write_end:1329: inode #12: comm repro: mark_inode_dirty error
[ 19.130171]
[ 19.130175] CPU: 1 UID: 0 PID: 251 Comm: repro Not tainted 6.12.51 #38
[ 19.130186] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
[ 19.130192] Call Trace:
[ 19.130196] <TASK>
[ 19.130199] dump_stack_lvl+0x7d/0xa0
[ 19.130224] print_report+0xcf/0x610
[ 19.130237] ? __virt_addr_valid+0xcb/0x320
[ 19.130250] ? ext4_ext_correct_indexes+0x41b/0x490
[ 19.130262] kasan_report+0xb5/0xe0
[ 19.130275] ? ext4_ext_correct_indexes+0x41b/0x490
[ 19.130287] ext4_ext_correct_indexes+0x41b/0x490
[ 19.130299] ? ext4_ext_try_to_merge+0x509/0x690
[ 19.130311] ext4_ext_insert_extent+0xb0f/0x3c00
[ 19.130325] ? __read_extent_tree_block+0x234/0x4c0
[ 19.130336] ? __pfx_ext4_mb_new_blocks+0x10/0x10
[ 19.130353] ? __pfx_ext4_ext_insert_extent+0x10/0x10
[ 19.130366] ? ext4_ext_search_right+0x2bf/0xb50
[ 19.130379] ext4_ext_map_blocks+0x1378/0x35b0
[ 19.130392] ? __pfx_ext4_ext_map_blocks+0x10/0x10
[ 19.130405] ? percpu_counter_add_batch+0x106/0x250
[ 19.130423] ? __pfx_percpu_counter_add_batch+0x10/0x10
[ 19.130440] ? __pfx_down_write+0x10/0x10
[ 19.130452] ? _raw_read_unlock+0x12/0x40
[ 19.130467] ? ext4_es_lookup_extent+0xb1/0x9a0
[ 19.130483] ext4_map_blocks+0x36a/0x10f0
[ 19.130499] ? find_get_block_common+0x4c7/0x880
[ 19.130512] ? __pfx_ext4_map_blocks+0x10/0x10
[ 19.130528] ? __crc32c_le_base+0x35c/0x5a0
[ 19.130541] ? bdev_getblk+0x5a/0x4b0
[ 19.130554] ? chksum_update+0x50/0xb0
[ 19.130565] ? ext4_inode_csum.isra.0+0x314/0x860
[ 19.130580] _ext4_get_block+0x1a7/0x510
[ 19.130596] ? __pfx__ext4_get_block+0x10/0x10
[ 19.130611] ? __pfx___filemap_add_folio+0x10/0x10
[ 19.130625] ? xas_start+0x100/0x350
[ 19.130638] ext4_get_block_unwritten+0x29/0xd0
[ 19.130654] ext4_block_write_begin+0x316/0xb90
[ 19.130664] ? __pfx_ext4_get_block_unwritten+0x10/0x10
[ 19.130680] ? deref_stack_reg+0x37/0x80
[ 19.130693] ? __pfx_ext4_block_write_begin+0x10/0x10
[ 19.130703] ? ext4_journal_check_start+0x165/0x270
[ 19.130713] ? folio_mapping+0xad/0x1c0
[ 19.130725] ext4_write_begin+0x51e/0xb20
[ 19.130735] ? inode_to_bdi+0x9c/0x140
[ 19.130747] ? __pfx_ext4_write_begin+0x10/0x10
[ 19.130758] ? arch_stack_walk+0x9d/0xf0
[ 19.130774] ext4_da_write_begin+0x53a/0x680
[ 19.130785] ? iter_file_splice_write+0x1b3/0xef0
[ 19.130797] ? __pfx_stack_trace_save+0x10/0x10
[ 19.130811] ? __pfx_ext4_da_write_begin+0x10/0x10
[ 19.130822] ? ext4_da_write_end+0x1df/0xaa0
[ 19.130833] ? timestamp_truncate+0x1b7/0x2a0
[ 19.130845] generic_perform_write+0x24f/0x700
[ 19.130856] ? __pfx_generic_perform_write+0x10/0x10
[ 19.130868] ? __pfx_down_write+0x10/0x10
[ 19.130880] ext4_buffered_write_iter+0xf8/0x350
[ 19.130897] ext4_file_write_iter+0x2e3/0x1130
[ 19.130914] ? __pfx_ext4_file_write_iter+0x10/0x10
[ 19.130929] ? kasan_save_track+0x14/0x30
[ 19.130940] ? __kasan_kmalloc+0x7f/0x90
[ 19.130951] ? splice_from_pipe_next.part.0+0x12e/0x430
[ 19.130964] iter_file_splice_write+0x8b2/0xef0
[ 19.130977] ? __pfx_iter_file_splice_write+0x10/0x10
[ 19.130992] ? ext4_file_splice_read+0xf4/0x140
[ 19.131007] ? __pfx_iter_file_splice_write+0x10/0x10
[ 19.131019] direct_splice_actor+0x181/0x5c0
[ 19.131030] splice_direct_to_actor+0x32d/0x920
[ 19.131042] ? __pfx_direct_splice_actor+0x10/0x10
[ 19.131054] ? __pfx_futex_wake_mark+0x10/0x10
[ 19.131068] ? __pfx_splice_direct_to_actor+0x10/0x10
[ 19.131081] ? __pfx_direct_splice_actor+0x10/0x10
[ 19.131092] do_splice_direct_actor+0x169/0x230
[ 19.131103] ? __pfx_do_splice_direct_actor+0x10/0x10
[ 19.131115] ? __pfx_direct_file_splice_eof+0x10/0x10
[ 19.131126] ? avc_policy_seqno+0x9/0x20
[ 19.131141] ? selinux_file_permission+0x30/0x480
[ 19.131155] do_splice_direct+0x41/0x60
[ 19.131166] ? __pfx_direct_splice_actor+0x10/0x10
[ 19.131177] do_sendfile+0x9e0/0xd10
[ 19.131188] ? __pfx_do_sendfile+0x10/0x10
[ 19.131198] ? __x64_sys_futex+0x1b1/0x400
[ 19.131210] ? __x64_sys_futex+0x1ba/0x400
[ 19.131223] __x64_sys_sendfile64+0x12e/0x1e0
[ 19.131236] ? __pfx___x64_sys_sendfile64+0x10/0x10
[ 19.131248] ? restore_fpregs_from_fpstate+0xcd/0x100
[ 19.131259] ? switch_fpu_return+0x100/0x230
[ 19.131269] do_syscall_64+0xaa/0x1b0
[ 19.131282] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 19.131298] RIP: 0033:0x456ded
[ 19.131305] Code: c3 e8 f7 29 00 00 0f 1f 80 00 00 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 88
[ 19.131316] RSP: 002b:00007efe43d4ad58 EFLAGS: 00000213 ORIG_RAX: 0000000000000028
[ 19.131326] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000456ded
[ 19.131332] RDX: 0000200000000180 RSI: 0000000000000004 RDI: 0000000000000004
[ 19.131339] RBP: 00007efe43d4ad80 R08: 0000000000000000 R09: 0000000000000000
[ 19.131344] R10: 000000000000c717 R11: 0000000000000213 R12: 00007fff68ae161e
[ 19.131351] R13: 00007fff68ae161f R14: 00007fff68ae16c0 R15: 00007efe43d4ae80
[ 19.131359] </TASK>
[ 24.218701] BUG: KASAN: slab-use-after-free in ext4_ext_correct_indexes+0x411/0x490
[ 24.219077] Read of size 8 at addr ffff8880092a67b0 by task repro/231
[ 24.219390]
[ 24.219502] CPU: 1 UID: 0 PID: 231 Comm: repro Not tainted 6.12.51 #38
[ 24.219509] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
[ 24.219513] Call Trace:
[ 24.219516] <TASK>
[ 24.219519] dump_stack_lvl+0x7d/0xa0
[ 24.219538] print_report+0xcf/0x610
[ 24.219547] ? __virt_addr_valid+0xcb/0x320
[ 24.219557] ? ext4_ext_correct_indexes+0x411/0x490
[ 24.219565] kasan_report+0xb5/0xe0
[ 24.219573] ? ext4_ext_correct_indexes+0x411/0x490
[ 24.219582] ext4_ext_correct_indexes+0x411/0x490
[ 24.219590] ? ext4_ext_try_to_merge+0x509/0x690
[ 24.219599] ext4_ext_insert_extent+0xb0f/0x3c00
[ 24.219608] ? __read_extent_tree_block+0x234/0x4c0
[ 24.219622] ? __pfx_ext4_mb_new_blocks+0x10/0x10
[ 24.219634] ? __pfx_ext4_ext_insert_extent+0x10/0x10
[ 24.219643] ? ext4_ext_search_right+0x2bf/0xb50
[ 24.219651] ext4_ext_map_blocks+0x1378/0x35b0
[ 24.219661] ? __pfx_ext4_ext_map_blocks+0x10/0x10
[ 24.219669] ? percpu_counter_add_batch+0x106/0x250
[ 24.219683] ? __pfx_percpu_counter_add_batch+0x10/0x10
[ 24.219694] ? __pfx_down_write+0x10/0x10
[ 24.219703] ? _raw_read_unlock+0x12/0x40
[ 24.219713] ? ext4_es_lookup_extent+0xb1/0x9a0
[ 24.219724] ext4_map_blocks+0x36a/0x10f0
[ 24.219735] ? find_get_block_common+0x4c7/0x880
[ 24.219745] ? __pfx_ext4_map_blocks+0x10/0x10
[ 24.219755] ? create_empty_buffers+0x2c/0x350
[ 24.219763] ? __crc32c_le_base+0x35c/0x5a0
[ 24.219773] ? bdev_getblk+0x5a/0x4b0
[ 24.219782] ? chksum_update+0x50/0xb0
[ 24.219789] ? ext4_inode_csum.isra.0+0x314/0x860
[ 24.219800] _ext4_get_block+0x1a7/0x510
[ 24.219811] ? __pfx__ext4_get_block+0x10/0x10
[ 24.219821] ? __pfx___filemap_add_folio+0x10/0x10
[ 24.219831] ? xas_start+0x100/0x350
[ 24.219840] ext4_get_block_unwritten+0x29/0xd0
[ 24.219851] ext4_block_write_begin+0x316/0xb90
[ 24.219858] ? __pfx_ext4_get_block_unwritten+0x10/0x10
[ 24.219869] ? deref_stack_reg+0x37/0x80
[ 24.219878] ? __pfx_ext4_block_write_begin+0x10/0x10
[ 24.219884] ? ext4_journal_check_start+0x165/0x270
[ 24.219891] ? folio_mapping+0xad/0x1c0
[ 24.219899] ext4_write_begin+0x51e/0xb20
[ 24.219907] ? inode_to_bdi+0x9c/0x140
[ 24.219915] ? __pfx_ext4_write_begin+0x10/0x10
[ 24.219922] ? arch_stack_walk+0x9d/0xf0
[ 24.219933] ext4_da_write_begin+0x53a/0x680
[ 24.219941] ? iter_file_splice_write+0x1b3/0xef0
[ 24.219949] ? __pfx_stack_trace_save+0x10/0x10
[ 24.219959] ? __pfx_ext4_da_write_begin+0x10/0x10
[ 24.219967] ? ext4_da_write_end+0x1df/0xaa0
[ 24.219974] ? timestamp_truncate+0x1b7/0x2a0
[ 24.219982] generic_perform_write+0x24f/0x700
[ 24.219990] ? __pfx_generic_perform_write+0x10/0x10
[ 24.219997] ? __pfx_down_write+0x10/0x10
[ 24.220006] ext4_buffered_write_iter+0xf8/0x350
[ 24.220018] ext4_file_write_iter+0x2e3/0x1130
[ 24.220029] ? __pfx_ext4_file_write_iter+0x10/0x10
[ 24.220040] ? kasan_save_track+0x14/0x30
[ 24.220048] ? __kasan_kmalloc+0x7f/0x90
[ 24.220055] ? splice_from_pipe_next.part.0+0x12e/0x430
[ 24.220064] iter_file_splice_write+0x8b2/0xef0
[ 24.220073] ? __pfx_iter_file_splice_write+0x10/0x10
[ 24.220083] ? ext4_file_splice_read+0xf4/0x140
[ 24.220094] ? __pfx_iter_file_splice_write+0x10/0x10
[ 24.220102] direct_splice_actor+0x181/0x5c0
[ 24.220110] splice_direct_to_actor+0x32d/0x920
[ 24.220118] ? __pfx_direct_splice_actor+0x10/0x10
[ 24.220126] ? __pfx_futex_wake_mark+0x10/0x10
[ 24.220136] ? __pfx_splice_direct_to_actor+0x10/0x10
[ 24.220145] ? __pfx_direct_splice_actor+0x10/0x10
[ 24.220152] do_splice_direct_actor+0x169/0x230
[ 24.220160] ? __pfx_do_splice_direct_actor+0x10/0x10
[ 24.220168] ? __pfx_direct_file_splice_eof+0x10/0x10
[ 24.220176] ? avc_policy_seqno+0x9/0x20
[ 24.220187] ? selinux_file_permission+0x30/0x480
[ 24.220197] do_splice_direct+0x41/0x60
[ 24.220204] ? __pfx_direct_splice_actor+0x10/0x10
[ 24.220212] do_sendfile+0x9e0/0xd10
[ 24.220219] ? __pfx_do_sendfile+0x10/0x10
[ 24.220226] ? __x64_sys_futex+0x1b1/0x400
[ 24.220235] ? __x64_sys_futex+0x1ba/0x400
[ 24.220243] __x64_sys_sendfile64+0x12e/0x1e0
[ 24.220252] ? __pfx___x64_sys_sendfile64+0x10/0x10
[ 24.220261] ? restore_fpregs_from_fpstate+0xcd/0x100
[ 24.220269] ? switch_fpu_return+0x100/0x230
[ 24.220276] do_syscall_64+0xaa/0x1b0
[ 24.220285] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 24.220296] RIP: 0033:0x456ded
[ 24.220301] Code: c3 e8 f7 29 00 00 0f 1f 80 00 00 00 00 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 88
[ 24.220308] RSP: 002b:00007f6ca0151d58 EFLAGS: 00000213 ORIG_RAX: 0000000000000028
[ 24.220315] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 0000000000456ded
[ 24.220320] RDX: 0000200000000180 RSI: 0000000000000004 RDI: 0000000000000004
[ 24.220324] RBP: 00007f6ca0151d80 R08: 0000000000000000 R09: 0000000000000000
[ 24.220328] R10: 000000000000c717 R11: 0000000000000213 R12: 00007ffdb5843c2e
[ 24.220332] R13: 00007ffdb5843c2f R14: 00007ffdb5843cd0 R15: 00007f6ca0151e80
[ 24.220337] </TASK>
Reproduce
----------
The issue is reproducible with a C program generated automatically. But this bug triggers on average only once every twenty runs or more, and each time requires restarting the QEMU. In my tests, the crash usually occurs within the first 40 seconds after QEMU starts. Therefore, if QEMU runs for more than 40 seconds, you can restart it for the next test.
The reproducer triggers the vulnerability through the following sequence:
1. Preparation: The program forks multiple child processes that continuously loop.
2. Mounting: It mounts a crafted ext4 image where a specific inode has a corrupted Extent Header.
3. Triggering: The processes execute file operations (like `sendfile` or `write`) on the corrupted file. These calls invoke `ext4_find_extent()`.
4. Vulnerability: `ext4_find_extent()` reads the invalid root header without validation. The subsequent binary search reads out-of-bounds data into the `path` array.
5. Crash: When `ext4_ext_correct_indexes()` (or similar) is called, it dereferences the invalid pointers in the `path` array, triggering the Slab-Use-After-Free.
I am attaching the full C program for convenience. Also, I have extracted the file system and attached it as image.img.
Security impact
---------------
This vulnerability allows a local unprivileged user to trigger a Use-After-Free (UAF) in the Linux kernel by mounting a crafted ext4 filesystem image.
The immediate impact is a Denial of Service (DoS) due to a kernel crash/panic. However, as this involves accessing a freed slab object, it may be exploitable for Local Privilege Escalation (LPE) if the attacker can groom the heap to replace the freed object with a controlled structure.
This attack surface is particularly relevant in environments where unprivileged users are permitted to mount filesystem images (e.g., via user namespaces).
Patch
--------------
From d048862ceac9b9c16842270b023d92f33b159ace Mon Sep 17 00:00:00 2001
From: 0ne1r0s <yuhaocheng035@...il.com>
Date: Tue, 9 Dec 2025 18:42:12 +0800
Subject: [PATCH] This patch fixes a use-after-free vulnerability in
`ext4_find_extent()` that occurs when processing a corrupted filesystem
image.
When traversing the extent tree, the kernel previously failed to validate the root extent header before using it. A corrupted root extent header (e.g., invalid `eh_entries`, `eh_depth`, etc.) can cause the binary search to calculate pointers beyond the allocated extent block or produce a path with invalid data. This leads to out-of-bounds memory access. Subsequently, when this corrupted path is used (e.g. in `ext4_ext_correct_indexes()`), it triggers a Use-After-Free or Slab-Out-Of-Bounds.
The fix adds a consistency check in `ext4_find_extent()` to validate the root extent header. It ensures that the header fields are valid before proceeding with the binary search. If the check fails, the function reports an error via `EXT4_ERROR_INODE` and returns `-EFSCORRUPTED`, preventing the unsafe memory access.
Signed-off-by: 0ne1r0s <yuhaocheng035@...il.com>
---
fs/ext4/extents.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 34e25eee6521..b09f664695f2 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -904,6 +904,9 @@ ext4_find_extent(struct inode *inode, ext4_lblk_t block,
goto err;
}
+ ret = ext4_ext_check(inode, eh, depth, 0);
+
if (ret) goto err;
+
if (path) {
ext4_ext_drop_refs(path);
if (depth > path[0].p_maxdepth) {
--
2.51.0
Request
-------
Could you please confirm if this is a known issue? And if it is considered a new security vulnerability, I would like to request or coordinate a CVE ID for it and will reference the relevant patch / mailing list thread in the CVE description.
Thank you very much for your time and for maintaining ext4.
Best regards,
Haocheng Yu
Zhejiang University
Download attachment ".config" of type "application/octet-stream" (143369 bytes)
Download attachment "ext4_find_extent.patch" of type "application/octet-stream" (1644 bytes)
Download attachment "image.img" of type "application/octet-stream" (65536 bytes)
View attachment "repro.c" of type "text/plain" (30128 bytes)
Powered by blists - more mailing lists