2024-04-24 11:39:40

by Sam Sun

[permalink] [raw]
Subject: [Linux kernel bug] KASAN: slab-out-of-bounds Read in xlog_cksum

Dear developers and maintainers,

We encountered a slab-out-of-bounds bug while using our modified
syzkaller. It was tested against the latest upstream kernel (6.9-rc5).
The kernel was compiled by clang 14.0.0, and kernel config and C repro
are attached to this email. Kernel crash log is listed below.
==================================================================
BUG: KASAN: slab-out-of-bounds in crc32c_intel_le_hw
arch/x86/crypto/crc32c-intel_glue.c:67 [inline]
BUG: KASAN: slab-out-of-bounds in crc32c_pcl_intel_update+0x129/0x280
arch/x86/crypto/crc32c-intel_glue.c:165
Read of size 8 at addr ffff88801ab45200 by task syz-executor207/8113

CPU: 0 PID: 8113 Comm: syz-executor207 Not tainted 6.9.0-rc5 #1
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0x201/0x300 lib/dump_stack.c:114
print_address_description+0x7b/0x360 mm/kasan/report.c:377
print_report+0xfd/0x1e0 mm/kasan/report.c:488
kasan_report+0xce/0x100 mm/kasan/report.c:601
crc32c_intel_le_hw arch/x86/crypto/crc32c-intel_glue.c:67 [inline]
crc32c_pcl_intel_update+0x129/0x280 arch/x86/crypto/crc32c-intel_glue.c:165
crc32c+0xdf/0x1b0 lib/libcrc32c.c:47
xlog_cksum+0xe9/0x150 fs/xfs/xfs_log.c:1829
xlog_recover_process+0x81/0x2e0 fs/xfs/xfs_log_recover.c:2864
xlog_do_recovery_pass+0x97e/0xfa0 fs/xfs/xfs_log_recover.c:3192
xlog_verify_tail+0x236/0x530 fs/xfs/xfs_log_recover.c:972
xlog_verify_head+0x275/0x530 fs/xfs/xfs_log_recover.c:1104
xlog_find_tail+0x92f/0xeb0 fs/xfs/xfs_log_recover.c:1319
xlog_recover+0x9f/0x540 fs/xfs/xfs_log_recover.c:3384
xfs_log_mount+0x27f/0x420 fs/xfs/xfs_log.c:718
xfs_mountfs+0xd15/0x1fb0 fs/xfs/xfs_mount.c:823
xfs_fs_fill_super+0x112e/0x13a0 fs/xfs/xfs_super.c:1730
get_tree_bdev+0x403/0x550 fs/super.c:1614
vfs_get_tree+0x90/0x2a0 fs/super.c:1779
do_new_mount+0x2bb/0xb40 fs/namespace.c:3352
do_mount fs/namespace.c:3692 [inline]
__do_sys_mount fs/namespace.c:3898 [inline]
__se_sys_mount+0x2c9/0x3b0 fs/namespace.c:3875
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xe4/0x240 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x67/0x6f
RIP: 0033:0x7f2eda11946e
Code: 48 c7 c0 ff ff ff ff eb aa e8 de 07 00 00 66 2e 0f 1f 84 00 00
00 00 00 0f 1f 40 00 f3 0f 1e fa 49 89 ca b8 a5 00 00 00 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:00007ffc4e98faa8 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5
RAX: ffffffffffffffda RBX: 0000555588012378 RCX: 00007f2eda11946e
RDX: 0000000020009600 RSI: 0000000020009640 RDI: 00007ffc4e98fac0
RBP: 00007ffc4e98fac0 R08: 00007ffc4e98fb00 R09: 00007ffc4e98fbff
R10: 0000000000010012 R11: 0000000000000246 R12: 0000000000010012
R13: 00007ffc4e98fb00 R14: 0000000000000003 R15: 0000000000000000
</TASK>

Allocated by task 8113:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x30/0x70 mm/kasan/common.c:68
poison_kmalloc_redzone mm/kasan/common.c:370 [inline]
__kasan_kmalloc+0x98/0xb0 mm/kasan/common.c:387
kasan_kmalloc include/linux/kasan.h:211 [inline]
__do_kmalloc_node mm/slub.c:3966 [inline]
__kmalloc_node+0x259/0x4f0 mm/slub.c:3973
kmalloc_node include/linux/slab.h:648 [inline]
kvmalloc_node+0x72/0x180 mm/util.c:634
xlog_do_recovery_pass+0x16e/0xfa0
xlog_verify_tail+0x236/0x530 fs/xfs/xfs_log_recover.c:972
xlog_verify_head+0x275/0x530 fs/xfs/xfs_log_recover.c:1104
xlog_find_tail+0x92f/0xeb0 fs/xfs/xfs_log_recover.c:1319
xlog_recover+0x9f/0x540 fs/xfs/xfs_log_recover.c:3384
xfs_log_mount+0x27f/0x420 fs/xfs/xfs_log.c:718
xfs_mountfs+0xd15/0x1fb0 fs/xfs/xfs_mount.c:823
xfs_fs_fill_super+0x112e/0x13a0 fs/xfs/xfs_super.c:1730
get_tree_bdev+0x403/0x550 fs/super.c:1614
vfs_get_tree+0x90/0x2a0 fs/super.c:1779
do_new_mount+0x2bb/0xb40 fs/namespace.c:3352
do_mount fs/namespace.c:3692 [inline]
__do_sys_mount fs/namespace.c:3898 [inline]
__se_sys_mount+0x2c9/0x3b0 fs/namespace.c:3875
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xe4/0x240 arch/x86/entry/common.c:83
entry_SYSCALL_64_after_hwframe+0x67/0x6f

The buggy address belongs to the object at ffff88801ab45000
which belongs to the cache kmalloc-512 of size 512
The buggy address is located 0 bytes to the right of
allocated 512-byte region [ffff88801ab45000, ffff88801ab45200)

The buggy address belongs to the physical page:
page: refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1ab44
head: order:2 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0xfff80000000840(slab|head|node=0|zone=1|lastcpupid=0xfff)
page_type: 0xffffffff()
raw: 00fff80000000840 ffff888013441c80 dead000000000100 dead000000000122
raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000
head: 00fff80000000840 ffff888013441c80 dead000000000100 dead000000000122
head: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000
head: 00fff80000000002 ffffea00006ad101 dead000000000122 00000000ffffffff
head: 0000000400000000 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected
page_owner tracks the page as allocated
page last allocated via order 2, migratetype Unmovable, gfp_mask
0xd20c0(__GFP_IO|__GFP_FS|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC),
pid 6481, tgid -1335316228 (systemd-udevd), ts 6481, free_ts
47750697502
set_page_owner include/linux/page_owner.h:32 [inline]
post_alloc_hook+0x1e6/0x210 mm/page_alloc.c:1534
prep_new_page mm/page_alloc.c:1541 [inline]
get_page_from_freelist+0x7d2/0x850 mm/page_alloc.c:3317
__alloc_pages+0x25e/0x580 mm/page_alloc.c:4575
__alloc_pages_node include/linux/gfp.h:238 [inline]
alloc_pages_node include/linux/gfp.h:261 [inline]
alloc_slab_page+0x6b/0x1a0 mm/slub.c:2175
allocate_slab+0x5d/0x200 mm/slub.c:2338
new_slab mm/slub.c:2391 [inline]
___slab_alloc+0xa50/0xe90 mm/slub.c:3525
__slab_alloc mm/slub.c:3610 [inline]
__slab_alloc_node mm/slub.c:3663 [inline]
slab_alloc_node mm/slub.c:3835 [inline]
__do_kmalloc_node mm/slub.c:3965 [inline]
__kmalloc_node+0x2dd/0x4f0 mm/slub.c:3973
kmalloc_array_node include/linux/slab.h:726 [inline]
kcalloc_node include/linux/slab.h:731 [inline]
memcg_alloc_slab_cgroups+0x80/0x120 mm/memcontrol.c:3015
account_slab mm/slub.c:2301 [inline]
allocate_slab+0x99/0x200 mm/slub.c:2356
new_slab mm/slub.c:2391 [inline]
___slab_alloc+0xa50/0xe90 mm/slub.c:3525
__slab_alloc mm/slub.c:3610 [inline]
__slab_alloc_node mm/slub.c:3663 [inline]
slab_alloc_node mm/slub.c:3835 [inline]
kmem_cache_alloc+0x24c/0x350 mm/slub.c:3852
anon_vma_chain_alloc mm/rmap.c:142 [inline]
anon_vma_fork+0x1e7/0x4d0 mm/rmap.c:365
dup_mmap+0xc26/0x1a40 kernel/fork.c:714
dup_mm kernel/fork.c:1688 [inline]
copy_mm+0x143/0x430 kernel/fork.c:1737
copy_process+0x1810/0x3d70 kernel/fork.c:2390
kernel_clone+0x228/0x6b0 kernel/fork.c:2797
page last free pid 5975 tgid 5975 stack trace:
reset_page_owner include/linux/page_owner.h:25 [inline]
free_pages_prepare mm/page_alloc.c:1141 [inline]
free_unref_page_prepare+0x72f/0x7c0 mm/page_alloc.c:2347
free_unref_page+0x37/0x3f0 mm/page_alloc.c:2487
discard_slab mm/slub.c:2437 [inline]
__put_partials+0x1a3/0x1e0 mm/slub.c:2906
put_cpu_partial+0x16a/0x240 mm/slub.c:2981
__slab_free+0x2ba/0x3d0 mm/slub.c:4151
qlink_free mm/kasan/quarantine.c:163 [inline]
qlist_free_all+0x60/0xd0 mm/kasan/quarantine.c:179
kasan_quarantine_reduce+0x15a/0x170 mm/kasan/quarantine.c:286
__kasan_slab_alloc+0x23/0x70 mm/kasan/common.c:322
kasan_slab_alloc include/linux/kasan.h:201 [inline]
slab_post_alloc_hook mm/slub.c:3798 [inline]
slab_alloc_node mm/slub.c:3845 [inline]
kmalloc_trace+0x16d/0x360 mm/slub.c:3992
kmalloc include/linux/slab.h:628 [inline]
kzalloc include/linux/slab.h:749 [inline]
kernfs_fop_open+0x3da/0xd00 fs/kernfs/file.c:623
do_dentry_open+0x8e7/0x1560 fs/open.c:955
do_open fs/namei.c:3642 [inline]
path_openat+0x285b/0x3200 fs/namei.c:3799
do_filp_open+0x268/0x4f0 fs/namei.c:3826
do_sys_openat2+0x12f/0x1c0 fs/open.c:1406
do_sys_open fs/open.c:1421 [inline]
__do_sys_openat fs/open.c:1437 [inline]
__se_sys_openat fs/open.c:1432 [inline]
__x64_sys_openat+0x247/0x290 fs/open.c:1432
do_syscall_x64 arch/x86/entry/common.c:52 [inline]
do_syscall_64+0xe4/0x240 arch/x86/entry/common.c:83

Memory state around the buggy address:
ffff88801ab45100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff88801ab45180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffff88801ab45200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
^
ffff88801ab45280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88801ab45300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
==================================================================
If you have any questions, please contact us.

Reported by Yue Sun <[email protected]>
Reported by xingwei lee <[email protected]>

Best Regards,
Yue


Attachments:
config (242.08 kB)
xlog_cksum.c (184.02 kB)
Download all attachments

2024-04-25 13:56:11

by Brian Foster

[permalink] [raw]
Subject: Re: [Linux kernel bug] KASAN: slab-out-of-bounds Read in xlog_cksum

On Thu, Apr 25, 2024 at 06:12:23AM -0700, Christoph Hellwig wrote:
> This triggers the workaround for really old xfsprogs putting in a
> bogus h_size:
>
> [ 12.101992] XFS (loop0): invalid iclog size (0 bytes), using lsunit (65536 bytes)
>
> but then calculates the log recovery buffer size based on the actual
> on-disk h_size value. The patch below open codes xlog_logrec_hblks and
> fixes this particular reproducer. But I wonder if we should limit the
> workaround. Brian, you don't happpen to remember how old xfsprogs had
> to be to require your workaround (commit a70f9fe52daa8)?
>

No, but a little digging turns up xfsprogs commit 20fbd4593ff2 ("libxfs:
format the log with valid log record headers"), which I think is what
you're looking for..? That went in around v4.5 or so, so I suppose
anything earlier than that is affected.

Brian

> diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
> index b445e8ce4a7d21..b3ea546508dc93 100644
> --- a/fs/xfs/xfs_log_recover.c
> +++ b/fs/xfs/xfs_log_recover.c
> @@ -2999,7 +2999,7 @@ xlog_do_recovery_pass(
> int error = 0, h_size, h_len;
> int error2 = 0;
> int bblks, split_bblks;
> - int hblks, split_hblks, wrapped_hblks;
> + int hblks = 1, split_hblks, wrapped_hblks;
> int i;
> struct hlist_head rhash[XLOG_RHASH_SIZE];
> LIST_HEAD (buffer_list);
> @@ -3055,14 +3055,16 @@ xlog_do_recovery_pass(
> if (error)
> goto bread_err1;
>
> - hblks = xlog_logrec_hblks(log, rhead);
> - if (hblks != 1) {
> - kvfree(hbp);
> - hbp = xlog_alloc_buffer(log, hblks);
> + if ((rhead->h_version & cpu_to_be32(XLOG_VERSION_2)) &&
> + h_size > XLOG_HEADER_CYCLE_SIZE) {
> + hblks = DIV_ROUND_UP(h_size, XLOG_HEADER_CYCLE_SIZE);
> + if (hblks > 1) {
> + kvfree(hbp);
> + hbp = xlog_alloc_buffer(log, hblks);
> + }
> }
> } else {
> ASSERT(log->l_sectBBsize == 1);
> - hblks = 1;
> hbp = xlog_alloc_buffer(log, 1);
> h_size = XLOG_BIG_RECORD_BSIZE;
> }
>


2024-04-25 18:11:51

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [Linux kernel bug] KASAN: slab-out-of-bounds Read in xlog_cksum

This triggers the workaround for really old xfsprogs putting in a
bogus h_size:

[ 12.101992] XFS (loop0): invalid iclog size (0 bytes), using lsunit (65536 bytes)

but then calculates the log recovery buffer size based on the actual
on-disk h_size value. The patch below open codes xlog_logrec_hblks and
fixes this particular reproducer. But I wonder if we should limit the
workaround. Brian, you don't happpen to remember how old xfsprogs had
to be to require your workaround (commit a70f9fe52daa8)?

diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index b445e8ce4a7d21..b3ea546508dc93 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -2999,7 +2999,7 @@ xlog_do_recovery_pass(
int error = 0, h_size, h_len;
int error2 = 0;
int bblks, split_bblks;
- int hblks, split_hblks, wrapped_hblks;
+ int hblks = 1, split_hblks, wrapped_hblks;
int i;
struct hlist_head rhash[XLOG_RHASH_SIZE];
LIST_HEAD (buffer_list);
@@ -3055,14 +3055,16 @@ xlog_do_recovery_pass(
if (error)
goto bread_err1;

- hblks = xlog_logrec_hblks(log, rhead);
- if (hblks != 1) {
- kvfree(hbp);
- hbp = xlog_alloc_buffer(log, hblks);
+ if ((rhead->h_version & cpu_to_be32(XLOG_VERSION_2)) &&
+ h_size > XLOG_HEADER_CYCLE_SIZE) {
+ hblks = DIV_ROUND_UP(h_size, XLOG_HEADER_CYCLE_SIZE);
+ if (hblks > 1) {
+ kvfree(hbp);
+ hbp = xlog_alloc_buffer(log, hblks);
+ }
}
} else {
ASSERT(log->l_sectBBsize == 1);
- hblks = 1;
hbp = xlog_alloc_buffer(log, 1);
h_size = XLOG_BIG_RECORD_BSIZE;
}

2024-04-26 06:13:14

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [Linux kernel bug] KASAN: slab-out-of-bounds Read in xlog_cksum

On Thu, Apr 25, 2024 at 09:57:54AM -0400, Brian Foster wrote:
> On Thu, Apr 25, 2024 at 06:12:23AM -0700, Christoph Hellwig wrote:
> > This triggers the workaround for really old xfsprogs putting in a
> > bogus h_size:
> >
> > [ 12.101992] XFS (loop0): invalid iclog size (0 bytes), using lsunit (65536 bytes)
> >
> > but then calculates the log recovery buffer size based on the actual
> > on-disk h_size value. The patch below open codes xlog_logrec_hblks and
> > fixes this particular reproducer. But I wonder if we should limit the
> > workaround. Brian, you don't happpen to remember how old xfsprogs had
> > to be to require your workaround (commit a70f9fe52daa8)?
> >
>
> No, but a little digging turns up xfsprogs commit 20fbd4593ff2 ("libxfs:
> format the log with valid log record headers"), which I think is what
> you're looking for..? That went in around v4.5 or so, so I suppose
> anything earlier than that is affected.

Thanks. I was kinda hoping we could exclude v5 file systems from that
workaround, but it is needed way too recent for that.

Maybe we can specificly check for the wrongly hardcoded
XLOG_HEADER_CYCLE_SIZE instead of allowing any value?


2024-04-26 12:44:14

by Brian Foster

[permalink] [raw]
Subject: Re: [Linux kernel bug] KASAN: slab-out-of-bounds Read in xlog_cksum

On Thu, Apr 25, 2024 at 11:13:05PM -0700, Christoph Hellwig wrote:
> On Thu, Apr 25, 2024 at 09:57:54AM -0400, Brian Foster wrote:
> > On Thu, Apr 25, 2024 at 06:12:23AM -0700, Christoph Hellwig wrote:
> > > This triggers the workaround for really old xfsprogs putting in a
> > > bogus h_size:
> > >
> > > [ 12.101992] XFS (loop0): invalid iclog size (0 bytes), using lsunit (65536 bytes)
> > >
> > > but then calculates the log recovery buffer size based on the actual
> > > on-disk h_size value. The patch below open codes xlog_logrec_hblks and
> > > fixes this particular reproducer. But I wonder if we should limit the
> > > workaround. Brian, you don't happpen to remember how old xfsprogs had
> > > to be to require your workaround (commit a70f9fe52daa8)?
> > >
> >
> > No, but a little digging turns up xfsprogs commit 20fbd4593ff2 ("libxfs:
> > format the log with valid log record headers"), which I think is what
> > you're looking for..? That went in around v4.5 or so, so I suppose
> > anything earlier than that is affected.
>
> Thanks. I was kinda hoping we could exclude v5 file systems from that
> workaround, but it is needed way too recent for that.
>
> Maybe we can specificly check for the wrongly hardcoded
> XLOG_HEADER_CYCLE_SIZE instead of allowing any value?
>

That seems like a reasonable option to me if you wanted to make it a bit
more limited to its purpose. You might just want to double check that
the size used in libxfs hadn't changed at any point previously, because
that 1. apparently wouldn't have been an issue up until the record
verification stuff and 2. the existing size-agnostic check in the kernel
would have still handled it (prior to being broken).

It might also be worth a separately named macro or something in the
kernel just for extra indication that this particular check is unique
and warrants extra thought on future changes. Not so much that I'd
expect the original macro value to change, but just that I suspect
something like that might have helped flag this logic as semi-special
and maybe helped avoid breaking it in commit 0c771b99d6c9. In hindsight,
maybe it would have been a little better even to just put that logic
into its own special fixup function or something. Anyways, just some
random thoughts..

Brian