hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8LHTR CVE: NA
--------------------------------
KASAN reported a UAF bug while fault injection test:
================================================================== BUG: KASAN: slab-use-after-free in __list_del_entry_valid_or_report+0x63/0x200 Read of size 8 at addr ffff888102aae0e8 by task kworker/2:1/48
CPU: 2 PID: 48 Comm: kworker/2:1 Not tainted 6.6.0-01347-g0684c49b89e7-dirty #8 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ?-20190727_073836-buildvm-ppc64le-16.ppc.fedoraproject.org-3.fc31 04/01/2014 Workqueue: xfs-reclaim/sda xfs_reclaim_worker Call Trace: <TASK> dump_stack_lvl+0x7f/0xb0 print_report+0x12b/0x930 kasan_report+0xc2/0x120 __asan_load8+0x9d/0x140 __list_del_entry_valid_or_report+0x63/0x200 xfs_iflush_abort_clean+0x8e/0x100 xfs_iflush_abort+0xa0/0x170 xfs_iflush_shutdown_abort+0x17a/0x220 xfs_icwalk_ag+0xa4b/0xed0 xfs_icwalk+0x97/0xf0 xfs_reclaim_worker+0x25/0x40 process_scheduled_works+0x3a8/0x950 worker_thread+0x302/0x710 kthread+0x1f1/0x270 ret_from_fork+0x52/0x70 ret_from_fork_asm+0x11/0x20 </TASK>
Allocated by task 733: kasan_save_stack+0x2a/0x60 kasan_set_track+0x2d/0x40 kasan_save_alloc_info+0x23/0x40 __kasan_slab_alloc+0x92/0xb0 kmem_cache_alloc+0x229/0xaf0 _xfs_buf_alloc+0x55/0x600 xfs_buf_get_map+0xc29/0x1b30 xfs_trans_get_buf_map+0x1bd/0x4b0 xfs_ialloc_inode_init+0x2e6/0x690 xfs_ialloc_ag_alloc+0x3ae/0xb00 xfs_dialloc+0x786/0xda0 xfs_create+0x4b7/0xaf0 xfs_generic_create+0x538/0x6f0 xfs_vn_create+0x1f/0x30 path_openat+0x13e2/0x2150 do_filp_open+0x16a/0x240 do_sys_openat2+0x3be/0x4e0 do_sys_open+0xa6/0x100 __x64_sys_open+0x52/0x60 do_syscall_64+0x39/0x80 entry_SYSCALL_64_after_hwframe+0x63/0xcd
Freed by task 0: kasan_save_stack+0x2a/0x60 kasan_set_track+0x2d/0x40 kasan_save_free_info+0x33/0x60 __kasan_slab_free+0x171/0x2b0 kmem_cache_free+0x313/0x8d0 xfs_buf_free_callback+0x64/0x80 rcu_do_batch+0x393/0xb10 rcu_core+0x599/0x930 rcu_core_si+0x16/0x20 __do_softirq+0x127/0x5a0
Last potentially related work creation: kasan_save_stack+0x2a/0x60 __kasan_record_aux_stack+0xba/0x100 kasan_record_aux_stack_noalloc+0x13/0x20 __call_rcu_common.constprop.0+0xb2/0xdd0 call_rcu+0x16/0x20 xfs_buf_free+0x70/0x1d0 xfs_buf_rele+0x44d/0xc00 xfs_buf_ioend+0x2f2/0x1350 xfs_buf_ioend_fail+0x77/0x190 __xfs_buf_submit+0x4cd/0x4e0 xfs_buf_delwri_submit_buffers+0x241/0x8b0 xfs_buf_delwri_submit_nowait+0x18/0x30 xfsaild+0x861/0x1720 kthread+0x1f1/0x270 ret_from_fork+0x52/0x70 ret_from_fork_asm+0x11/0x20
Second to last potentially related work creation: kasan_save_stack+0x2a/0x60 __kasan_record_aux_stack+0xba/0x100 kasan_record_aux_stack_noalloc+0x13/0x20 insert_work+0x2d/0x160 __queue_work+0x7b1/0x9d0 queue_work_on+0x91/0xa0 xfs_buf_bio_end_io+0x191/0x1a0 bio_endio+0x403/0x440 blk_update_request+0x228/0xa90 scsi_end_request+0x59/0x310 scsi_io_completion+0xec/0xbb0 scsi_finish_command+0x18d/0x2b0 scsi_complete+0xd2/0x1f0 blk_complete_reqs+0x9e/0xc0 blk_done_softirq+0x25/0x30 __do_softirq+0x127/0x5a0
The buggy address belongs to the object at ffff888102aae000 which belongs to the cache xfs_buf of size 376 The buggy address is located 232 bytes inside of freed 376-byte region [ffff888102aae000, ffff888102aae178)
The buggy address belongs to the physical page: page:000000005e670ef6 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x102aae head:000000005e670ef6 order:1 entire_mapcount:0 nr_pages_mapped:0 pincount:0 flags: 0x2fffff80000840(slab|head|node=0|zone=2|lastcpupid=0x1fffff) page_type: 0xffffffff() raw: 002fffff80000840 ffff8881009c32c0 dead000000000122 0000000000000000 raw: 0000000000000000 0000000080120012 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected
Memory state around the buggy address: ffff888102aadf80: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc ffff888102aae000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888102aae080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^ ffff888102aae100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fc ffff888102aae180: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb ================================================================== This is a low probability problem, it tooks me a long time to find the process that the problem occurred:
1. When creating a new file, if there are no free inodes, we need to allocate a new chunk. The buf item and inode items associated with inode will be submitted to CIL independently. If all goes well, both the buf item and the inode item will be inserted into the AIL, and the buf item will be in front of the inode item.
2. At the first time, xfsaild only pushed buf item. If an error occurs while writing back the inode buffer, the inode item will be set XFS_LI_FAILED in xfs_buf_inode_io_fail() when buf io end, and the buf item will remain in the AIL.
3. At the second time, xfsaild only pushed buf item again, while writing back the inode buffer and the log has shut down, the inode buffer will be set XBF_STALE and the buf item is removed from AIL when buf io end. Because of inode is not flushed, ili_last_fields in xfs_inode is still 0, so inode item will left in AIL.
4. Concurrently, a new transaction log inode that in the same cluster as the previous inode, it will get the same inode buffer in xfs_buf_find(), _XBF_INODES flag will be cleared in xfs_buf_find() due to buffer is staled.
5. At the third time, xfsaild push the inode item that has marked XFS_LI_FAILED, AIL will resubmit the inode item in xfsaild_resubmit_item(). It will go to the wrong code path due to inode buffer missing _XBF_INODES flag, all inode items that in bp->b_li_list will be reduced the references to buffer, and inode item's li_buf set to null, but inode item still in bp->b_li_list. After all reference count decreasing the inode buffer will be freed.
6. When xfs reclaim inode, remove inode item from bp->b_li_list will cause a uaf xfs_iflush_abort_clean().
Fix it by add xfs shutdown condition check in xfs_buf_find_lock(), if it has been shutdown, it is useless to get the buffer. While the inode item is still reference to the inode buffer, the _XBF_INODES flag will not be missing.
Signed-off-by: Long Li leo.lilong@huawei.com --- fs/xfs/xfs_buf.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index c1ece4a08ff4..34f8fcf72e33 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -554,6 +554,11 @@ xfs_buf_find_lock( XFS_STATS_INC(bp->b_mount, xb_get_locked_waited); }
+ if (xlog_is_shutdown(bp->b_mount->m_log)) { + xfs_buf_unlock(bp); + return -EIO; + } + /* * if the buffer is stale, clear all the external state associated with * it. We need to keep flags such as how we allocated the buffer memory