[syzbot] [ext4?] KASAN: slab-out-of-bounds Read in get_max_inline_xattr_value_size

13 views
Skip to first unread message

syzbot

unread,
Mar 30, 2023, 3:48:51 AM3/30/23
to adilger...@dilger.ca, linux...@vger.kernel.org, linux-...@vger.kernel.org, linux-...@vger.kernel.org, syzkall...@googlegroups.com, ty...@mit.edu
Hello,

syzbot found the following issue on:

HEAD commit: da8e7da11e4b Merge tag 'nfsd-6.3-4' of git://git.kernel.or..
git tree: upstream
console+strace: https://syzkaller.appspot.com/x/log.txt?x=114fae51c80000
kernel config: https://syzkaller.appspot.com/x/.config?x=acdb62bf488a8fe5
dashboard link: https://syzkaller.appspot.com/bug?extid=1966db24521e5f6e23f7
compiler: Debian clang version 15.0.7, GNU ld (GNU Binutils for Debian) 2.35.2
syz repro: https://syzkaller.appspot.com/x/repro.syz?x=1597fd0ec80000
C reproducer: https://syzkaller.appspot.com/x/repro.c?x=14149471c80000

Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/62e9c5f4bead/disk-da8e7da1.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/c11aa933e2a7/vmlinux-da8e7da1.xz
kernel image: https://storage.googleapis.com/syzbot-assets/7a21bdd49c84/bzImage-da8e7da1.xz
mounted in repro: https://storage.googleapis.com/syzbot-assets/58216d4aadcf/mount_0.gz

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

EXT4-fs (loop0): mounted filesystem 00000000-0000-0000-0000-000000000000 without journal. Quota mode: none.
EXT4-fs error (device loop0): ext4_xattr_ibody_get:669: inode #18: comm syz-executor366: corrupted in-inode xattr: bad magic number in in-inode xattr
==================================================================
BUG: KASAN: slab-use-after-free in get_max_inline_xattr_value_size+0x369/0x510 fs/ext4/inline.c:62
Read of size 4 at addr ffff88807c4ac084 by task syz-executor366/5076

CPU: 0 PID: 5076 Comm: syz-executor366 Not tainted 6.3.0-rc3-syzkaller-00338-gda8e7da11e4b #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/02/2023
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x1e7/0x2d0 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:319 [inline]
print_report+0x163/0x540 mm/kasan/report.c:430
kasan_report+0x176/0x1b0 mm/kasan/report.c:536
get_max_inline_xattr_value_size+0x369/0x510 fs/ext4/inline.c:62
ext4_get_max_inline_size+0x141/0x200 fs/ext4/inline.c:113
ext4_prepare_inline_data+0x87/0x1d0 fs/ext4/inline.c:393
ext4_da_write_inline_data_begin+0x208/0xe40 fs/ext4/inline.c:931
ext4_da_write_begin+0x4da/0x960 fs/ext4/inode.c:3064
generic_perform_write+0x300/0x5e0 mm/filemap.c:3926
ext4_buffered_write_iter+0x122/0x3a0 fs/ext4/file.c:289
ext4_file_write_iter+0x1d6/0x1930
call_write_iter include/linux/fs.h:1851 [inline]
new_sync_write fs/read_write.c:491 [inline]
vfs_write+0x7b2/0xbb0 fs/read_write.c:584
ksys_write+0x1a0/0x2c0 fs/read_write.c:637
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
RIP: 0033:0x7f63c54aea99
Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 11 15 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 c0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fff3f17f0c8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f63c54aea99
RDX: 0000000000000010 RSI: 0000000020000100 RDI: 0000000000000004
RBP: 0000000000000000 R08: 00007fff3f17f0f0 R09: 00007fff3f17f0f0
R10: 00007fff3f17f0f0 R11: 0000000000000246 R12: 00007f63c546d960
R13: 00007fff3f17f120 R14: 00007fff3f17f100 R15: 0000000000000000
</TASK>

Allocated by task 4998:
kasan_save_stack mm/kasan/common.c:45 [inline]
kasan_set_track+0x4f/0x70 mm/kasan/common.c:52
__kasan_slab_alloc+0x66/0x70 mm/kasan/common.c:328
kasan_slab_alloc include/linux/kasan.h:186 [inline]
slab_post_alloc_hook+0x68/0x3a0 mm/slab.h:769
slab_alloc_node mm/slub.c:3452 [inline]
slab_alloc mm/slub.c:3460 [inline]
__kmem_cache_alloc_lru mm/slub.c:3467 [inline]
kmem_cache_alloc+0x11f/0x2e0 mm/slub.c:3476
anon_vma_chain_alloc mm/rmap.c:141 [inline]
anon_vma_fork+0x1fa/0x580 mm/rmap.c:364
dup_mmap kernel/fork.c:660 [inline]
dup_mm kernel/fork.c:1545 [inline]
copy_mm+0xae3/0x1670 kernel/fork.c:1594
copy_process+0x1905/0x3fc0 kernel/fork.c:2264
kernel_clone+0x222/0x800 kernel/fork.c:2679
__do_sys_clone kernel/fork.c:2820 [inline]
__se_sys_clone kernel/fork.c:2804 [inline]
__x64_sys_clone+0x235/0x280 kernel/fork.c:2804
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd

Freed by task 5013:
kasan_save_stack mm/kasan/common.c:45 [inline]
kasan_set_track+0x4f/0x70 mm/kasan/common.c:52
kasan_save_free_info+0x2b/0x40 mm/kasan/generic.c:521
____kasan_slab_free+0xd6/0x120 mm/kasan/common.c:236
kasan_slab_free include/linux/kasan.h:162 [inline]
slab_free_hook mm/slub.c:1781 [inline]
slab_free_freelist_hook mm/slub.c:1807 [inline]
slab_free mm/slub.c:3787 [inline]
kmem_cache_free+0x297/0x520 mm/slub.c:3809
anon_vma_chain_free mm/rmap.c:146 [inline]
unlink_anon_vmas+0x59e/0x5f0 mm/rmap.c:447
free_pgtables+0x348/0x4f0 mm/memory.c:383
exit_mmap+0x2c1/0x850 mm/mmap.c:3040
__mmput+0x115/0x3c0 kernel/fork.c:1204
exit_mm+0x227/0x310 kernel/exit.c:563
do_exit+0x612/0x2290 kernel/exit.c:856
do_group_exit+0x206/0x2c0 kernel/exit.c:1019
__do_sys_exit_group kernel/exit.c:1030 [inline]
__se_sys_exit_group kernel/exit.c:1028 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1028
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd

The buggy address belongs to the object at ffff88807c4ac070
which belongs to the cache anon_vma_chain of size 80
The buggy address is located 20 bytes inside of
freed 80-byte region [ffff88807c4ac070, ffff88807c4ac0c0)

The buggy address belongs to the physical page:
page:ffffea0001f12b00 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x7c4ac
flags: 0xfff00000000200(slab|node=0|zone=1|lastcpupid=0x7ff)
raw: 00fff00000000200 ffff888140007280 dead000000000122 0000000000000000
raw: 0000000000000000 0000000000240024 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 0, migratetype Unmovable, gfp_mask 0x12800(GFP_NOWAIT|__GFP_NOWARN|__GFP_NORETRY), pid 4998, tgid 4998 (dhcpcd-run-hook), ts 47082738820, free_ts 47079213294
prep_new_page mm/page_alloc.c:2553 [inline]
get_page_from_freelist+0x3246/0x33c0 mm/page_alloc.c:4326
__alloc_pages+0x255/0x670 mm/page_alloc.c:5592
alloc_slab_page+0x6a/0x160 mm/slub.c:1851
allocate_slab mm/slub.c:1998 [inline]
new_slab+0x84/0x2f0 mm/slub.c:2051
___slab_alloc+0xa85/0x10a0 mm/slub.c:3193
__slab_alloc mm/slub.c:3292 [inline]
__slab_alloc_node mm/slub.c:3345 [inline]
slab_alloc_node mm/slub.c:3442 [inline]
slab_alloc mm/slub.c:3460 [inline]
__kmem_cache_alloc_lru mm/slub.c:3467 [inline]
kmem_cache_alloc+0x1b9/0x2e0 mm/slub.c:3476
anon_vma_chain_alloc mm/rmap.c:141 [inline]
anon_vma_clone+0x98/0x4d0 mm/rmap.c:288
anon_vma_fork+0x87/0x580 mm/rmap.c:351
dup_mmap kernel/fork.c:660 [inline]
dup_mm kernel/fork.c:1545 [inline]
copy_mm+0xae3/0x1670 kernel/fork.c:1594
copy_process+0x1905/0x3fc0 kernel/fork.c:2264
kernel_clone+0x222/0x800 kernel/fork.c:2679
__do_sys_clone kernel/fork.c:2820 [inline]
__se_sys_clone kernel/fork.c:2804 [inline]
__x64_sys_clone+0x235/0x280 kernel/fork.c:2804
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
page last free stack trace:
reset_page_owner include/linux/page_owner.h:24 [inline]
free_pages_prepare mm/page_alloc.c:1454 [inline]
free_pcp_prepare mm/page_alloc.c:1504 [inline]
free_unref_page_prepare+0xe2f/0xe70 mm/page_alloc.c:3388
free_unref_page_list+0x596/0x830 mm/page_alloc.c:3529
release_pages+0x219e/0x2470 mm/swap.c:1042
tlb_batch_pages_flush mm/mmu_gather.c:97 [inline]
tlb_flush_mmu_free mm/mmu_gather.c:292 [inline]
tlb_flush_mmu+0x100/0x210 mm/mmu_gather.c:299
tlb_finish_mmu+0xd4/0x1f0 mm/mmu_gather.c:391
exit_mmap+0x2c9/0x850 mm/mmap.c:3042
__mmput+0x115/0x3c0 kernel/fork.c:1204
exit_mm+0x227/0x310 kernel/exit.c:563
do_exit+0x612/0x2290 kernel/exit.c:856
do_group_exit+0x206/0x2c0 kernel/exit.c:1019
__do_sys_exit_group kernel/exit.c:1030 [inline]
__se_sys_exit_group kernel/exit.c:1028 [inline]
__x64_sys_exit_group+0x3f/0x40 kernel/exit.c:1028
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd

Memory state around the buggy address:
ffff88807c4abf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff88807c4ac000: fa fb fb fb fb fb fb fb fb fb fc fc fc fc fa fb
>ffff88807c4ac080: fb fb fb fb fb fb fb fb fc fc fc fc fa fb fb fb
^
ffff88807c4ac100: fb fb fb fb fb fb fc fc fc fc fa fb fb fb fb fb
ffff88807c4ac180: fb fb fb fb fc fc fc fc fa 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.
syzbot can test patches for this issue, for details see:
https://goo.gl/tpsmEJ#testing-patches

Jan Kara

unread,
Apr 21, 2023, 4:44:34 AM4/21/23
to syzbot, adilger...@dilger.ca, linux...@vger.kernel.org, linux-...@vger.kernel.org, linux-...@vger.kernel.org, syzkall...@googlegroups.com, ty...@mit.edu
The problem seems to be that get_max_inline_xattr_value_size() is iterating
xattr space like:

for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
if (!entry->e_value_inum && entry->e_value_size) {
size_t offs = le16_to_cpu(entry->e_value_offs);
if (offs < min_offs)
min_offs = offs;
}
}

without checking for validity of the structures and we can reach this path
without verifying xattrs are valid. Perhaps we should verify in-inode xattr
data as part for __ext4_iget()?

Honza
--
Jan Kara <ja...@suse.com>
SUSE Labs, CR

Theodore Ts'o

unread,
May 11, 2023, 8:12:03 PM5/11/23
to syzbot, syzkall...@googlegroups.com
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d3dfc51a43c5..82869266cdc7 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -58,6 +58,10 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
header = IHDR(inode, raw_inode);
entry = IFIRST(header);

+ WARN_ON(ext4_xattr_check_inode(inode, header,
+ (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size));
+
+
/* Compute min_offs. */
for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
if (!entry->e_value_inum && entry->e_value_size) {
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index dfc2e223bd10..d5ea058f63a1 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -315,6 +315,17 @@ __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
#define xattr_check_inode(inode, header, end) \
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)

+int ext4_xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
+ void *end)
+{
+ int error = xattr_check_inode(inode, header, end);
+
+ WARN_ON(error);
+ return error;
+}
+
+
+
static int
xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry,
void *end, int name_index, const char *name, int sorted)
@@ -667,6 +678,7 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
header = IHDR(inode, raw_inode);
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
error = xattr_check_inode(inode, header, end);
+ WARN_ON(error);
if (error)
goto cleanup;
entry = IFIRST(header);
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 824faf0b15a8..8b6350693fdc 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -225,3 +225,6 @@ static inline void ext4_xattr_inode_set_class(struct inode *ea_inode) { }
#endif

extern int ext4_get_inode_usage(struct inode *inode, qsize_t *usage);
+extern int ext4_xattr_check_inode(struct inode *inode,
+ struct ext4_xattr_ibody_header *header,
+ void *end);

syzbot

unread,
May 11, 2023, 8:44:20 PM5/11/23
to syzkall...@googlegroups.com, ty...@mit.edu
Hello,

syzbot has tested the proposed patch but the reproducer is still triggering an issue:
WARNING in ext4_xattr_ibody_get

loop0: detected capacity change from 0 to 2048
EXT4-fs (loop0): mounted filesystem 00000000-0000-0000-0000-000000000000 without journal. Quota mode: none.
EXT4-fs error (device loop0): ext4_xattr_ibody_get:680: inode #18: comm syz-executor.0: corrupted in-inode xattr: bad magic number in in-inode xattr
------------[ cut here ]------------
WARNING: CPU: 1 PID: 5419 at fs/ext4/xattr.c:681 ext4_xattr_ibody_get+0x654/0x740
Modules linked in:
CPU: 1 PID: 5419 Comm: syz-executor.0 Not tainted 6.4.0-rc1-syzkaller-00013-g3b217639bd26-dirty #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/28/2023
RIP: 0010:ext4_xattr_ibody_get+0x654/0x740 fs/ext4/xattr.c:681
Code: 25 28 00 00 00 48 3b 84 24 c0 00 00 00 0f 85 f1 00 00 00 44 89 f0 48 8d 65 d8 5b 41 5c 41 5d 41 5e 41 5f 5d c3 e8 8c 2e 3e ff <0f> 0b e9 7c ff ff ff e8 80 2e 3e ff 41 be de ff ff ff e9 67 ff ff
RSP: 0018:ffffc9000561f5e0 EFLAGS: 00010293
RAX: ffffffff824d1044 RBX: ffffc9000561f660 RCX: ffff888023ea3b80
RDX: 0000000000000000 RSI: 00000000ffffff8b RDI: 0000000000000000
RBP: ffffc9000561f700 R08: ffffffff824d0c79 R09: ffffed100eab7bb4
R10: 0000000000000000 R11: dffffc0000000001 R12: dffffc0000000000
R13: 1ffff92000ac3ec8 R14: 00000000ffffff8b R15: ffff88807a4cc1a4
FS: 00007fc48aa6b700(0000) GS:ffff8880b9900000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000020003000 CR3: 000000007a477000 CR4: 00000000003506e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
ext4_xattr_get+0x131/0x840 fs/ext4/xattr.c:739
__vfs_getxattr+0x436/0x470 fs/xattr.c:424
cap_inode_need_killpriv+0x45/0x60 security/commoncap.c:301
security_inode_need_killpriv+0x61/0x90 security/security.c:2329
dentry_needs_remove_privs fs/inode.c:1967 [inline]
__file_remove_privs+0x252/0x640 fs/inode.c:1998
file_modified_flags+0x106/0x480 fs/inode.c:2117
ext4_write_checks+0x24a/0x2c0 fs/ext4/file.c:268
ext4_buffered_write_iter+0xbd/0x3a0 fs/ext4/file.c:284
ext4_file_write_iter+0x1d6/0x1930
call_write_iter include/linux/fs.h:1868 [inline]
new_sync_write fs/read_write.c:491 [inline]
vfs_write+0x790/0xb20 fs/read_write.c:584
ksys_write+0x1a0/0x2c0 fs/read_write.c:637
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
RIP: 0033:0x7fc489c8c0f9
Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 f1 19 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:00007fc48aa6b168 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007fc489dabf80 RCX: 00007fc489c8c0f9
RDX: 0000000000000010 RSI: 0000000020000100 RDI: 0000000000000004
RBP: 00007fc489ce7b39 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007ffeecb3103f R14: 00007fc48aa6b300 R15: 0000000000022000
</TASK>


Tested on:

commit: 3b217639 ext4: fix deadlock when converting an inline ..
git tree: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
console output: https://syzkaller.appspot.com/x/log.txt?x=11a05dc6280000
kernel config: https://syzkaller.appspot.com/x/.config?x=7f2e5c3b3ba61823
dashboard link: https://syzkaller.appspot.com/bug?extid=1966db24521e5f6e23f7
compiler: Debian clang version 15.0.7, GNU ld (GNU Binutils for Debian) 2.35.2
patch: https://syzkaller.appspot.com/x/patch.diff?x=15b97aae280000

Theodore Ts'o

unread,
May 12, 2023, 3:18:50 AM5/12/23
to syzbot, syzkall...@googlegroups.com
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d3dfc51a43c5..5a3d2144880b 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -58,6 +58,10 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
header = IHDR(inode, raw_inode);
entry = IFIRST(header);

+ if (ext4_xattr_check_inode(inode, header,
+ (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size))
+ return 0;
+
/* Compute min_offs. */
for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
if (!entry->e_value_inum && entry->e_value_size) {
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index dfc2e223bd10..2ac0c8d9ed4d 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -315,6 +315,18 @@ __xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
#define xattr_check_inode(inode, header, end) \
__xattr_check_inode((inode), (header), (end), __func__, __LINE__)

+int ext4_xattr_check_inode(struct inode *inode, struct ext4_xattr_ibody_header *header,
+ void *end)
+{
+ int error = xattr_check_inode(inode, header, end);
+
+ if (error) {
+ EXT4_ERROR_INODE(inode, "xattr_check_inode returns %d", error);
+ WARN_ON(1);
+ }
+ return error;
+}
+
static int
xattr_find_entry(struct inode *inode, struct ext4_xattr_entry **pentry,
void *end, int name_index, const char *name, int sorted)
@@ -667,8 +679,11 @@ ext4_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
header = IHDR(inode, raw_inode);
end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;
error = xattr_check_inode(inode, header, end);
- if (error)
+ if (error) {
+ EXT4_ERROR_INODE(inode, "xattr_check_inode returns %d", error);
+ WARN_ON(1);
goto cleanup;
+ }
entry = IFIRST(header);
error = xattr_find_entry(inode, &entry, end, name_index, name, 0);
if (error)

syzbot

unread,
May 12, 2023, 4:28:43 AM5/12/23
to syzkall...@googlegroups.com, ty...@mit.edu
Hello,

syzbot has tested the proposed patch but the reproducer is still triggering an issue:
WARNING in ext4_xattr_ibody_get

EXT4-fs error (device loop0): ext4_xattr_ibody_get:681: inode #18: comm syz-executor.0: corrupted in-inode xattr: bad magic number in in-inode xattr
EXT4-fs error (device loop0): ext4_xattr_ibody_get:683: inode #18: comm syz-executor.0: xattr_check_inode returns -117
------------[ cut here ]------------
WARNING: CPU: 0 PID: 5401 at fs/ext4/xattr.c:684 ext4_xattr_ibody_get+0x2be/0x780 fs/ext4/xattr.c:683
Modules linked in:
CPU: 0 PID: 5401 Comm: syz-executor.0 Not tainted 6.4.0-rc1-syzkaller-00013-g3b217639bd26-dirty #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/28/2023
RIP: 0010:ext4_xattr_ibody_get+0x2be/0x780 fs/ext4/xattr.c:684
Code: 3e ff 48 8b 7c 24 10 48 c7 c6 70 f0 84 8c ba ab 02 00 00 31 c9 45 31 c0 49 c7 c1 60 d1 fd 8a 41 56 e8 36 64 fc ff 48 83 c4 08 <0f> 0b 48 8b 44 24 50 42 80 3c 20 00 74 08 48 89 df e8 7c ec 95 ff
RSP: 0018:ffffc900048a75e0 EFLAGS: 00010292
RAX: e450bee5393ebb00 RBX: ffffc900048a7660 RCX: ffff88807ab38000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: ffffc900048a7700 R08: ffffffff82496f2d R09: ffffed100e9a043b
R10: 0000000000000000 R11: dffffc0000000001 R12: dffffc0000000000
R13: 1ffff92000914ec8 R14: 00000000ffffff8b R15: ffff8880214b21a4
FS: 00007fb4e9ba4700(0000) GS:ffff8880b9800000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055b29512f048 CR3: 0000000071600000 CR4: 00000000003506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
<TASK>
ext4_xattr_get+0x131/0x840 fs/ext4/xattr.c:742
__vfs_getxattr+0x436/0x470 fs/xattr.c:424
cap_inode_need_killpriv+0x45/0x60 security/commoncap.c:301
security_inode_need_killpriv+0x61/0x90 security/security.c:2329
dentry_needs_remove_privs fs/inode.c:1967 [inline]
__file_remove_privs+0x252/0x640 fs/inode.c:1998
file_modified_flags+0x106/0x480 fs/inode.c:2117
ext4_write_checks+0x24a/0x2c0 fs/ext4/file.c:268
ext4_buffered_write_iter+0xbd/0x3a0 fs/ext4/file.c:284
ext4_file_write_iter+0x1d6/0x1930
call_write_iter include/linux/fs.h:1868 [inline]
new_sync_write fs/read_write.c:491 [inline]
vfs_write+0x790/0xb20 fs/read_write.c:584
ksys_write+0x1a0/0x2c0 fs/read_write.c:637
do_syscall_x64 arch/x86/entry/common.c:50 [inline]
do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80
entry_SYSCALL_64_after_hwframe+0x63/0xcd
RIP: 0033:0x7fb4e8e8c0f9
Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 f1 19 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:00007fb4e9ba4168 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007fb4e8fabf80 RCX: 00007fb4e8e8c0f9
RDX: 0000000000000010 RSI: 0000000020000100 RDI: 0000000000000004
RBP: 00007fb4e8ee7b39 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fff076ead3f R14: 00007fb4e9ba4300 R15: 0000000000022000
</TASK>


Tested on:

commit: 3b217639 ext4: fix deadlock when converting an inline ..
git tree: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
console output: https://syzkaller.appspot.com/x/log.txt?x=158b877a280000
kernel config: https://syzkaller.appspot.com/x/.config?x=7f2e5c3b3ba61823
dashboard link: https://syzkaller.appspot.com/bug?extid=1966db24521e5f6e23f7
compiler: Debian clang version 15.0.7, GNU ld (GNU Binutils for Debian) 2.35.2
patch: https://syzkaller.appspot.com/x/patch.diff?x=10b18eea280000

Theodore Ts'o

unread,
May 12, 2023, 12:14:27 PM5/12/23
to syzbot, syzkall...@googlegroups.com
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d3dfc51a43c5..bf4cb9bf877a 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -34,6 +34,7 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
struct ext4_xattr_ibody_header *header;
struct ext4_xattr_entry *entry;
struct ext4_inode *raw_inode;
+ void *end;
int free, min_offs;

if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
@@ -57,9 +58,17 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
raw_inode = ext4_raw_inode(iloc);
header = IHDR(inode, raw_inode);
entry = IFIRST(header);
+ end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;

/* Compute min_offs. */
- for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
+ while (!IS_LAST_ENTRY(entry)) {
+ void *next = EXT4_XATTR_NEXT(e);
+
+ if (next >= end) {
+ EXT4_ERROR_INODE(inode,
+ "corrupt xattr in inline inode");
+ return 0;
+ }
if (!entry->e_value_inum && entry->e_value_size) {

syzbot

unread,
May 12, 2023, 12:51:31 PM5/12/23
to syzkall...@googlegroups.com, ty...@mit.edu
Hello,

syzbot tried to test the proposed patch but the build/boot failed:

fs/ext4/inline.c:65:32: error: use of undeclared identifier 'e'


Tested on:

commit: 3b217639 ext4: fix deadlock when converting an inline ..
git tree: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
dashboard link: https://syzkaller.appspot.com/bug?extid=1966db24521e5f6e23f7
compiler: Debian clang version 15.0.7, GNU ld (GNU Binutils for Debian) 2.35.2
patch: https://syzkaller.appspot.com/x/patch.diff?x=15745d24280000

Theodore Ts'o

unread,
May 12, 2023, 1:39:54 PM5/12/23
to syzbot, syzkall...@googlegroups.com
#syz test: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index d3dfc51a43c5..f47adb284e90 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -34,6 +34,7 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
struct ext4_xattr_ibody_header *header;
struct ext4_xattr_entry *entry;
struct ext4_inode *raw_inode;
+ void *end;
int free, min_offs;

if (!EXT4_INODE_HAS_XATTR_SPACE(inode))
@@ -57,14 +58,23 @@ static int get_max_inline_xattr_value_size(struct inode *inode,
raw_inode = ext4_raw_inode(iloc);
header = IHDR(inode, raw_inode);
entry = IFIRST(header);
+ end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size;

/* Compute min_offs. */
- for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
+ while (!IS_LAST_ENTRY(entry)) {
+ void *next = EXT4_XATTR_NEXT(entry);
+
+ if (next >= end) {
+ EXT4_ERROR_INODE(inode,
+ "corrupt xattr in inline inode");
+ return 0;
+ }
if (!entry->e_value_inum && entry->e_value_size) {
size_t offs = le16_to_cpu(entry->e_value_offs);
if (offs < min_offs)
min_offs = offs;
}
+ entry = next;
}
free = min_offs -
((void *)entry - (void *)IFIRST(header)) - sizeof(__u32);

syzbot

unread,
May 12, 2023, 2:18:19 PM5/12/23
to syzkall...@googlegroups.com, ty...@mit.edu
Hello,

syzbot has tested the proposed patch and the reproducer did not trigger any issue:

Reported-and-tested-by: syzbot+1966db...@syzkaller.appspotmail.com

Tested on:

commit: 3b217639 ext4: fix deadlock when converting an inline ..
git tree: https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
console output: https://syzkaller.appspot.com/x/log.txt?x=1693964e280000
kernel config: https://syzkaller.appspot.com/x/.config?x=7f2e5c3b3ba61823
dashboard link: https://syzkaller.appspot.com/bug?extid=1966db24521e5f6e23f7
compiler: Debian clang version 15.0.7, GNU ld (GNU Binutils for Debian) 2.35.2
patch: https://syzkaller.appspot.com/x/patch.diff?x=15deb562280000

Note: testing is done by a robot and is best-effort only.

Theodore Ts'o

unread,
May 12, 2023, 5:28:42 PM5/12/23
to Jan Kara, syzbot, adilger...@dilger.ca, linux...@vger.kernel.org, syzkall...@googlegroups.com
On Fri, Apr 21, 2023 at 10:44:31AM +0200, Jan Kara wrote:
> The problem seems to be that get_max_inline_xattr_value_size() is iterating
> xattr space like:
>
> for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) {
> if (!entry->e_value_inum && entry->e_value_size) {
> size_t offs = le16_to_cpu(entry->e_value_offs);
> if (offs < min_offs)
> min_offs = offs;
> }
> }
>
> without checking for validity of the structures and we can reach this path
> without verifying xattrs are valid. Perhaps we should verify in-inode xattr
> data as part for __ext4_iget()?

We do check that the xattrs are valid when the inline file is opened,
and we would reject the open in that case, so the write system call
would never be able to operate on the corrupted inode.... except when
the reproducer has opened the block device and starts scribbling on
the inode table.

The only way we can stop this particular out-of-bounds read is to add
bounds checking in the above loop. Patch follows (which has been
tested by syzbot).

- Ted
Reply all
Reply to author
Forward
0 new messages