When converting an inline file to non-inline, if e_value_offs is changed underneath the filesystem by some change in the block device, it will lead to an out-of-bounds access that KASAN detects as an UAF. [ 17.272876] ================================================================== [ 17.273620] BUG: KASAN: slab-use-after-free in ext4_read_inline_data+0x19c/0x270 [ 17.274286] Read of size 20 at addr ffff8880099821a3 by task repro/690 [ 17.274949] [ 17.275487] CPU: 0 UID: 0 PID: 690 Comm: repro Not tainted 6.12.0-rc7-00078-g94f739ba5406 #164 [ 17.276372] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014 [ 17.277186] Call Trace: [ 17.277524] <TASK> [ 17.277824] dump_stack_lvl+0xf3/0x140 [ 17.278391] ? __pfx_dump_stack_lvl+0x10/0x10 [ 17.278949] ? _printk+0xa7/0xf0 [ 17.279381] ? __virt_addr_valid+0x142/0x340 [ 17.279919] print_report+0x163/0x4d0 [ 17.280381] ? __virt_addr_valid+0x142/0x340 [ 17.280882] ? __virt_addr_valid+0x2d9/0x340 [ 17.281286] ? ext4_read_inline_data+0x19c/0x270 [ 17.281720] kasan_report+0x99/0xd0 [ 17.282024] ? ext4_read_inline_data+0x19c/0x270 [ 17.282407] kasan_check_range+0x16a/0x170 [ 17.282686] ? ext4_read_inline_data+0x19c/0x270 [ 17.283364] __asan_memcpy+0x25/0x70 [ 17.283883] ext4_read_inline_data+0x19c/0x270 [ 17.284354] ext4_convert_inline_data_nolock+0x15b/0x7c0 [ 17.284893] ? find_held_lock+0x41/0x1d0 [ 17.285326] ? ext4_convert_inline_data+0x2f6/0x4d0 [ 17.285833] ? __pfx_ext4_convert_inline_data_nolock+0x10/0x10 [ 17.286390] ? ext4_convert_inline_data+0x26b/0x4d0 [ 17.286752] ? __pfx_down_write+0x10/0x10 [ 17.287037] ? ext4_journal_check_start+0x5e/0x1c0 [ 17.287342] ? __ext4_journal_start_sb+0x288/0x420 [ 17.287677] ext4_convert_inline_data+0x3aa/0x4d0 [ 17.288007] ? __pfx_ext4_convert_inline_data+0x10/0x10 [ 17.288373] ? lock_is_held_type+0x91/0x130 [ 17.288659] ext4_fallocate+0xd0/0x1840 [ 17.288927] vfs_fallocate+0x380/0x4d0 [ 17.289233] ksys_fallocate+0x33/0x60 [ 17.289532] __x64_sys_fallocate+0x92/0xb0 [ 17.289863] do_syscall_64+0xb6/0x160 [ 17.290170] ? clear_bhb_loop+0x45/0xa0 [ 17.290498] ? clear_bhb_loop+0x45/0xa0 [ 17.290828] entry_SYSCALL_64_after_hwframe+0x76/0x7e [ 17.291265] RIP: 0033:0x7806623d0c7d [ 17.291583] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 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 8b 0d 73 81 0d 00 f7 d8 64 89 01 48 [ 17.294082] RSP: 002b:00007ffd763fc498 EFLAGS: 00000203 ORIG_RAX: 000000000000011d [ 17.294838] RAX: ffffffffffffffda RBX: 00007ffd763fc5d8 RCX: 00007806623d0c7d [ 17.295697] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000004 [ 17.296665] RBP: 00007ffd763fc4c0 R08: 0000000000000000 R09: 0000000000000000 [ 17.297621] R10: 0000000000008000 R11: 0000000000000203 R12: 0000000000000000 [ 17.298364] R13: 00007ffd763fc5e8 R14: 00005af91214ecc8 R15: 0000780662518000 [ 17.299062] </TASK> Calling ext4_xattr_ibody_find after reading the inode with ext4_get_inode_loc will lead to a check of the validity of the xattrs, avoiding this problem. Reported-by: syzbot+f6a8aa7e307cf6ee835e@xxxxxxxxxxxxxxxxxxxxxxxxx Closes: https://syzkaller.appspot.com/bug?extid=f6a8aa7e307cf6ee835e Fixes: 0c8d414f163f ("ext4: let fallocate handle inline data correctly") Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@xxxxxxxxxx> --- fs/ext4/inline.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index 3536ca7e4fcc..401a224f0956 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -2022,9 +2022,15 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline) int ext4_convert_inline_data(struct inode *inode) { + struct ext4_xattr_ibody_find is = { + .s = { .not_found = -ENODATA, }, + }; + struct ext4_xattr_info i = { + .name_index = EXT4_XATTR_INDEX_SYSTEM, + .name = EXT4_XATTR_SYSTEM_DATA, + }; int error, needed_blocks, no_expand; handle_t *handle; - struct ext4_iloc iloc; if (!ext4_has_inline_data(inode)) { ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); @@ -2045,8 +2051,8 @@ int ext4_convert_inline_data(struct inode *inode) needed_blocks = ext4_writepage_trans_blocks(inode); - iloc.bh = NULL; - error = ext4_get_inode_loc(inode, &iloc); + is.iloc.bh = NULL; + error = ext4_get_inode_loc(inode, &is.iloc); if (error) return error; @@ -2057,11 +2063,17 @@ int ext4_convert_inline_data(struct inode *inode) } ext4_write_lock_xattr(inode, &no_expand); + + error = ext4_xattr_ibody_find(inode, &i, &is); + if (error) + goto out_xattr; + if (ext4_has_inline_data(inode)) - error = ext4_convert_inline_data_nolock(handle, inode, &iloc); + error = ext4_convert_inline_data_nolock(handle, inode, &is.iloc); +out_xattr: ext4_write_unlock_xattr(inode, &no_expand); ext4_journal_stop(handle); out_free: - brelse(iloc.bh); + brelse(is.iloc.bh); return error; } -- 2.34.1