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: use-after-free in __list_del_entry_valid+0x2b1/0x2c0 Read of size 8 at addr ffff888023edb888 by task kworker/0:1/34
CPU: 0 PID: 34 Comm: kworker/0:1 Not tainted 5.10.0-07305-g4c00b418452b-dirty #369 Workqueue: xfs-reclaim/sda xfs_reclaim_worker Call Trace: dump_stack+0x115/0x16b print_address_description.constprop.0+0x2c/0x450 kasan_report.cold+0x5d/0xdb __asan_report_load8_noabort+0x20/0x30 __list_del_entry_valid+0x2b1/0x2c0 xfs_iflush_abort_clean+0x11c/0x290 xfs_iflush_abort+0xd2/0x2c0 xfs_iflush_shutdown_abort+0x2e3/0x580 xfs_icwalk_ag+0xe9d/0x1a00 xfs_reclaim_worker+0x29/0x50 process_one_work+0x71f/0x11d0 worker_thread+0x5cb/0x10a0 kthread+0x35b/0x490 ret_from_fork+0x1f/0x30
Allocated by task 642: kasan_save_stack+0x23/0x60 __kasan_kmalloc.constprop.0+0xd9/0x140 kasan_slab_alloc+0x12/0x20 kmem_cache_alloc+0x1c4/0xa50 _xfs_buf_alloc+0x72/0xd50 xfs_buf_get_map+0x156/0x7c0 xfs_trans_get_buf_map+0x41c/0x8c0 xfs_ialloc_inode_init+0x455/0xaf0 xfs_ialloc_ag_alloc+0x71f/0x1790 xfs_dialloc+0x3f9/0x8a0 xfs_ialloc+0x12e/0x1970 xfs_dir_ialloc+0x144/0x730 xfs_create+0x623/0xe80 xfs_generic_create+0x571/0x820 xfs_vn_create+0x31/0x40 path_openat+0x209d/0x3b10 do_filp_open+0x1c2/0x2e0 do_sys_openat2+0x4fc/0x900 do_sys_open+0xd8/0x150 __x64_sys_open+0x87/0xd0 do_syscall_64+0x45/0x70 entry_SYSCALL_64_after_hwframe+0x61/0xc6
The buggy address belongs to the object at ffff888023edb780 which belongs to the cache xfs_buf of size 392 The buggy address is located 264 bytes inside of 392-byte region [ffff888023edb780, ffff888023edb908) The buggy address belongs to the page: page:ffffea00008fb600 refcount:1 mapcount:0 mapping:0000000000000000 index:0xffff888023edb780 pfn:0x23ed8 head:ffffea00008fb600 order:2 compound_mapcount:0 compound_pincount:0 flags: 0x1fffff80010200(slab|head|node=0|zone=1|lastcpupid=0x1fffff) raw: 001fffff80010200 ffffea00008fb008 ffff888019016050 ffff888018ff4300 raw: ffff888023edb780 000000000013000d 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected
Memory state around the buggy address: ffff888023edb780: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff888023edb800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888023edb880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^ ffff888023edb900: fb fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff888023edb980: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ==================================================================
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(), 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..54f10d914b46 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_relse(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