Skip to content

Commit c6b72f5

Browse files
Thadeu Lima de Souza Cascardotytso
authored andcommitted
ext4: avoid OOB when system.data xattr changes underneath the filesystem
When looking up for an entry in an inlined directory, 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. EXT4-fs (loop0): mounted filesystem 00000000-0000-0000-0000-000000000000 r/w without journal. Quota mode: none. loop0: detected capacity change from 2048 to 2047 ================================================================== BUG: KASAN: use-after-free in ext4_search_dir+0xf2/0x1c0 fs/ext4/namei.c:1500 Read of size 1 at addr ffff88803e91130f by task syz-executor269/5103 CPU: 0 UID: 0 PID: 5103 Comm: syz-executor269 Not tainted 6.11.0-rc4-syzkaller #0 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2~bpo12+1 04/01/2014 Call Trace: <TASK> __dump_stack lib/dump_stack.c:93 [inline] dump_stack_lvl+0x241/0x360 lib/dump_stack.c:119 print_address_description mm/kasan/report.c:377 [inline] print_report+0x169/0x550 mm/kasan/report.c:488 kasan_report+0x143/0x180 mm/kasan/report.c:601 ext4_search_dir+0xf2/0x1c0 fs/ext4/namei.c:1500 ext4_find_inline_entry+0x4be/0x5e0 fs/ext4/inline.c:1697 __ext4_find_entry+0x2b4/0x1b30 fs/ext4/namei.c:1573 ext4_lookup_entry fs/ext4/namei.c:1727 [inline] ext4_lookup+0x15f/0x750 fs/ext4/namei.c:1795 lookup_one_qstr_excl+0x11f/0x260 fs/namei.c:1633 filename_create+0x297/0x540 fs/namei.c:3980 do_symlinkat+0xf9/0x3a0 fs/namei.c:4587 __do_sys_symlinkat fs/namei.c:4610 [inline] __se_sys_symlinkat fs/namei.c:4607 [inline] __x64_sys_symlinkat+0x95/0xb0 fs/namei.c:4607 do_syscall_x64 arch/x86/entry/common.c:52 [inline] do_syscall_64+0xf3/0x230 arch/x86/entry/common.c:83 entry_SYSCALL_64_after_hwframe+0x77/0x7f RIP: 0033:0x7f3e73ced469 Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 21 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 b8 ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fff4d40c258 EFLAGS: 00000246 ORIG_RAX: 000000000000010a RAX: ffffffffffffffda RBX: 0032656c69662f2e RCX: 00007f3e73ced469 RDX: 0000000020000200 RSI: 00000000ffffff9c RDI: 00000000200001c0 RBP: 0000000000000000 R08: 00007fff4d40c290 R09: 00007fff4d40c290 R10: 0023706f6f6c2f76 R11: 0000000000000246 R12: 00007fff4d40c27c R13: 0000000000000003 R14: 431bde82d7b634db R15: 00007fff4d40c2b0 </TASK> Calling ext4_xattr_ibody_find right 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+0c2508114d912a54ee79@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0c2508114d912a54ee79 Fixes: e8e948e ("ext4: let ext4_find_entry handle inline data") Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@igalia.com> Link: https://patch.msgid.link/20240821152324.3621860-5-cascardo@igalia.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
1 parent 51e14e7 commit c6b72f5

File tree

1 file changed

+21
-10
lines changed

1 file changed

+21
-10
lines changed

fs/ext4/inline.c

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,25 +1664,36 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
16641664
struct ext4_dir_entry_2 **res_dir,
16651665
int *has_inline_data)
16661666
{
1667+
struct ext4_xattr_ibody_find is = {
1668+
.s = { .not_found = -ENODATA, },
1669+
};
1670+
struct ext4_xattr_info i = {
1671+
.name_index = EXT4_XATTR_INDEX_SYSTEM,
1672+
.name = EXT4_XATTR_SYSTEM_DATA,
1673+
};
16671674
int ret;
1668-
struct ext4_iloc iloc;
16691675
void *inline_start;
16701676
int inline_size;
16711677

1672-
ret = ext4_get_inode_loc(dir, &iloc);
1678+
ret = ext4_get_inode_loc(dir, &is.iloc);
16731679
if (ret)
16741680
return ERR_PTR(ret);
16751681

16761682
down_read(&EXT4_I(dir)->xattr_sem);
1683+
1684+
ret = ext4_xattr_ibody_find(dir, &i, &is);
1685+
if (ret)
1686+
goto out;
1687+
16771688
if (!ext4_has_inline_data(dir)) {
16781689
*has_inline_data = 0;
16791690
goto out;
16801691
}
16811692

1682-
inline_start = (void *)ext4_raw_inode(&iloc)->i_block +
1693+
inline_start = (void *)ext4_raw_inode(&is.iloc)->i_block +
16831694
EXT4_INLINE_DOTDOT_SIZE;
16841695
inline_size = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DOTDOT_SIZE;
1685-
ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
1696+
ret = ext4_search_dir(is.iloc.bh, inline_start, inline_size,
16861697
dir, fname, 0, res_dir);
16871698
if (ret == 1)
16881699
goto out_find;
@@ -1692,23 +1703,23 @@ struct buffer_head *ext4_find_inline_entry(struct inode *dir,
16921703
if (ext4_get_inline_size(dir) == EXT4_MIN_INLINE_DATA_SIZE)
16931704
goto out;
16941705

1695-
inline_start = ext4_get_inline_xattr_pos(dir, &iloc);
1706+
inline_start = ext4_get_inline_xattr_pos(dir, &is.iloc);
16961707
inline_size = ext4_get_inline_size(dir) - EXT4_MIN_INLINE_DATA_SIZE;
16971708

1698-
ret = ext4_search_dir(iloc.bh, inline_start, inline_size,
1709+
ret = ext4_search_dir(is.iloc.bh, inline_start, inline_size,
16991710
dir, fname, 0, res_dir);
17001711
if (ret == 1)
17011712
goto out_find;
17021713

17031714
out:
1704-
brelse(iloc.bh);
1715+
brelse(is.iloc.bh);
17051716
if (ret < 0)
1706-
iloc.bh = ERR_PTR(ret);
1717+
is.iloc.bh = ERR_PTR(ret);
17071718
else
1708-
iloc.bh = NULL;
1719+
is.iloc.bh = NULL;
17091720
out_find:
17101721
up_read(&EXT4_I(dir)->xattr_sem);
1711-
return iloc.bh;
1722+
return is.iloc.bh;
17121723
}
17131724

17141725
int ext4_delete_inline_entry(handle_t *handle,

0 commit comments

Comments
 (0)