Offering: HULK hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I73AXQ
--------------------------------
syzkaller found a UAF:
================================================================== BUG: KASAN: use-after-free in __rb_erase_augmented include/linux/rbtree_augmented.h:225 [inline] BUG: KASAN: use-after-free in rb_erase+0x16e/0x690 lib/rbtree.c:443 Write of size 8 at addr ffff888101990a40 by task kworker/1:1H/114 CPU: 1 PID: 114 Comm: kworker/1:1H Not tainted 5.10.0-00734-gc980ff0a1f18-dirty #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.15.0-0-g2dd4b9b3f840-prebuilt.qemu.org 04/01/2014 Workqueue: xfs-log/sda xlog_ioend_work Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xbe/0xfd lib/dump_stack.c:118 kasan_report+0x3a/0x50 mm/kasan/report.c:559 __rb_erase_augmented include/linux/rbtree_augmented.h:225 [inline] rb_erase+0x16e/0x690 lib/rbtree.c:443 xfs_extent_busy_clear_one+0x5a/0x1c0 fs/xfs/xfs_extent_busy.c:517 xfs_extent_busy_clear+0x18b/0x1d0 fs/xfs/xfs_extent_busy.c:569 xlog_cil_committed+0x12a/0x370 fs/xfs/xfs_log_cil.c:659 xlog_cil_process_committed+0xbc/0xe0 fs/xfs/xfs_log_cil.c:683 xlog_state_do_iclog_callbacks+0x30c/0x4b0 fs/xfs/xfs_log.c:2777 xlog_state_do_callback+0x99/0x150 fs/xfs/xfs_log.c:2802 xlog_ioend_work+0x57/0xc0 fs/xfs/xfs_log.c:1308 process_one_work+0x406/0x810 kernel/workqueue.c:2280 worker_thread+0x96/0x720 kernel/workqueue.c:2426 kthread+0x1f4/0x250 kernel/kthread.c:313 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:299
Allocated by task 22679: kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48 kasan_set_track mm/kasan/common.c:56 [inline] set_alloc_info mm/kasan/common.c:498 [inline] __kasan_kmalloc mm/kasan/common.c:530 [inline] __kasan_kmalloc.constprop.0+0xf0/0x130 mm/kasan/common.c:501 kmalloc include/linux/slab.h:568 [inline] kmem_alloc+0xc2/0x230 fs/xfs/kmem.c:21 kmem_zalloc fs/xfs/kmem.h:69 [inline] xfs_extent_busy_insert+0x3c/0x370 fs/xfs/xfs_extent_busy.c:36 __xfs_free_extent+0x268/0x340 fs/xfs/libxfs/xfs_alloc.c:3327 xfs_free_extent fs/xfs/libxfs/xfs_alloc.h:183 [inline] xfs_ag_extend_space+0x26e/0x280 fs/xfs/libxfs/xfs_ag.c:540 xfs_growfs_data_private.isra.0+0x64e/0x6f0 fs/xfs/xfs_fsops.c:112 xfs_growfs_data+0x287/0x360 fs/xfs/xfs_fsops.c:239 xfs_file_ioctl+0x9f2/0x1320 fs/xfs/xfs_ioctl.c:2274 vfs_ioctl fs/ioctl.c:48 [inline] __do_sys_ioctl fs/ioctl.c:753 [inline] __se_sys_ioctl+0x111/0x160 fs/ioctl.c:739 do_syscall_64+0x30/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x61/0xc6
Freed by task 114: kasan_save_stack+0x1b/0x40 mm/kasan/common.c:48 kasan_set_track+0x1c/0x30 mm/kasan/common.c:56 kasan_set_free_info+0x20/0x40 mm/kasan/generic.c:361 __kasan_slab_free.part.0+0x13f/0x1b0 mm/kasan/common.c:482 slab_free_hook mm/slub.c:1569 [inline] slab_free_freelist_hook mm/slub.c:1608 [inline] slab_free mm/slub.c:3179 [inline] kfree+0xce/0x860 mm/slub.c:4176 kvfree+0x47/0x50 mm/util.c:647 xfs_extent_busy_clear+0x18b/0x1d0 fs/xfs/xfs_extent_busy.c:569 xlog_cil_committed+0x12a/0x370 fs/xfs/xfs_log_cil.c:659 xlog_cil_process_committed+0xbc/0xe0 fs/xfs/xfs_log_cil.c:683 xlog_state_do_iclog_callbacks+0x30c/0x4b0 fs/xfs/xfs_log.c:2777 xlog_state_do_callback+0x99/0x150 fs/xfs/xfs_log.c:2802 xlog_ioend_work+0x57/0xc0 fs/xfs/xfs_log.c:1308 process_one_work+0x406/0x810 kernel/workqueue.c:2280 worker_thread+0x96/0x720 kernel/workqueue.c:2426 kthread+0x1f4/0x250 kernel/kthread.c:313 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:299
The buggy address belongs to the object at ffff888101990a40 which belongs to the cache kmalloc-64 of size 64 The buggy address is located 0 bytes inside of 64-byte region [ffff888101990a40, ffff888101990a80) The buggy address belongs to the page: page:ffffea0004066400 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x101990 head:ffffea0004066400 order:1 compound_mapcount:0 flags: 0x17ffffc0010200(slab|head|node=0|zone=2|lastcpupid=0x1fffff) raw: 0017ffffc0010200 ffffea00009d5d88 ffff888100040a70 ffff88810004d500 raw: 0000000000000000 0000000000100010 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected
Memory state around the buggy address: ffff888101990900: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888101990980: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff888101990a00: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
^ ffff888101990a80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888101990b00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ==================================================================
The bug can be reproduced with the following sequence:
# truncate -s 1073741824 xfs_test.img # mkfs.xfs -f -b size=1024 -d agcount=4 xfs_test.img # truncate -s 2305843009213693952 xfs_test.img # mount -o loop xfs_test.img /mnt/test # fsstress -d /mnt/test -l 0 -n 10000 >/dev/null & # xfs_growfs -D 1125899907891200 /mnt/test
The root cause is that during growfs, user space passed in a large value of newblcoks to xfs_growfs_data_private(), due to current sb_agblocks is too small, new AG count will exceed UINT_MAX. Because of AG number type is unsigned int and it would overflow, that caused nagcount much smaller than the actual value and new blocks in the old last AG very large. When old last AG expand the space, xfs_extlen_t type is unsigned int, it would overflow again, if new blocks exceed UINT_MAX and the lower 32 bit are zero. This will cause busy extent whose length is equal to zero insert into rbtree, xfs_extent_busy_clear_one() will free it abormally but not remove it from tbree, UAF will be triggered when access rbtree on the next time. Fix it by add checks for nagcount overflow inxfs_growfs_data_private.
Signed-off-by: Long Li leo.lilong@huawei.com --- fs/xfs/xfs_fsops.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 1430b2b9c466..e81e052dcb75 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -54,6 +54,9 @@ xfs_growfs_data_private( new = nb; /* use new as a temporary here */ nb_mod = do_div(new, mp->m_sb.sb_agblocks); nagcount = new + (nb_mod != 0); + /* check for overflow */ + if (nagcount < new) + return -EINVAL; if (nb_mod && nb_mod < XFS_MIN_AG_BLOCKS) { nagcount--; nb = (xfs_rfsblock_t)nagcount * mp->m_sb.sb_agblocks;