2024-01-17 09:21:40

by Ubisectech Sirius

[permalink] [raw]
Subject: KASAN: slab-use-after-free Read in gfs2_invalidate_folio

Hello.
We are Ubisectech Sirius Team, the vulnerability lab of China ValiantSec. Recently, our team has discovered a issue in Linux kernel 6.7.0-g052d534373b7. Attached to the email were a POC file of the issue.
Stack dump:
[ 183.852987][ T8017] ==================================================================
[ 183.854073][ T8017] BUG: KASAN: slab-use-after-free in gfs2_invalidate_folio (./include/linux/list.h:373 fs/gfs2/aops.c:617 fs/gfs2/aops.c:655)
[ 183.855102][ T8017] Read of size 8 at addr ffff88801bc1d1f8 by task poc/8017
[ 183.855924][ T8017]
[ 183.856224][ T8017] CPU: 0 PID: 8017 Comm: poc Not tainted 6.7.0-g052d534373b7 #19
[ 183.857158][ T8017] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 183.858307][ T8017] Call Trace:
[ 183.858691][ T8017] <TASK>
[ 183.859066][ T8017] dump_stack_lvl (lib/dump_stack.c:107)
[ 183.859651][ T8017] print_report (mm/kasan/report.c:378 mm/kasan/report.c:488)
[ 183.861450][ T8017] kasan_report (mm/kasan/report.c:603)
[ 183.862716][ T8017] gfs2_invalidate_folio (./include/linux/list.h:373 fs/gfs2/aops.c:617 fs/gfs2/aops.c:655)
[ 183.864722][ T8017] truncate_cleanup_folio (mm/truncate.c:158 mm/truncate.c:178)
[ 183.865415][ T8017] truncate_inode_pages_range (mm/truncate.c:357 (discriminator 3))
[ 183.873444][ T8017] gfs2_evict_inode (fs/gfs2/super.c:1509)
[ 183.880349][ T8017] evict (fs/inode.c:670)
[ 183.880853][ T8017] iput.part.0 (fs/inode.c:1767)
[ 183.882098][ T8017] iput (fs/inode.c:1767)
[ 183.882579][ T8017] gfs2_put_super (fs/gfs2/super.c:627 (discriminator 3))
[ 183.883843][ T8017] generic_shutdown_super (fs/super.c:652)
[ 183.884510][ T8017] kill_block_super (fs/super.c:1681)
[ 183.885099][ T8017] gfs2_kill_sb (fs/gfs2/ops_fstype.c:1805)
[ 183.885672][ T8017] deactivate_locked_super (fs/super.c:479)
[ 183.886390][ T8017] deactivate_super (fs/super.c:512)
[ 183.886963][ T8017] cleanup_mnt (fs/namespace.c:144 fs/namespace.c:1268)
[ 183.887513][ T8017] task_work_run (kernel/task_work.c:182 (discriminator 1))
[ 183.888749][ T8017] do_exit (kernel/exit.c:872)
[ 183.892789][ T8017] do_group_exit (kernel/exit.c:1001)
[ 183.893332][ T8017] __x64_sys_exit_group (kernel/exit.c:1029)
[ 183.893929][ T8017] do_syscall_64 (arch/x86/entry/common.c:52 arch/x86/entry/common.c:83)
[ 183.894477][ T8017] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:129)
[ 183.895184][ T8017] RIP: 0033:0x7f8cfe81dbd9
[ 183.895741][ T8017] Code: Unable to access opcode bytes at 0x7f8cfe81dbaf.
[ 183.902301][ T8017] </TASK>
[ 183.902578][ T8017]
[ 183.902856][ T8017] Allocated by task 8017:
[ 183.903369][ T8017] kasan_save_stack (mm/kasan/common.c:48)
[ 183.903924][ T8017] kasan_save_track (./arch/x86/include/asm/current.h:42 mm/kasan/common.c:60 mm/kasan/common.c:70)
[ 183.904513][ T8017] __kasan_slab_alloc (mm/kasan/common.c:314 mm/kasan/common.c:340)
[ 183.905073][ T8017] kmem_cache_alloc (mm/slub.c:3814 mm/slub.c:3860 mm/slub.c:3867)
[ 183.905666][ T8017] gfs2_trans_add_data (fs/gfs2/trans.c:169 fs/gfs2/trans.c:209)
[ 183.906263][ T8017] gfs2_unstuff_dinode (fs/gfs2/bmap.c:81 fs/gfs2/bmap.c:166)
[ 183.906859][ T8017] gfs2_adjust_quota (fs/gfs2/quota.c:880)
[ 183.907440][ T8017] do_sync (fs/gfs2/quota.c:992)
[ 183.907938][ T8017] gfs2_quota_sync (fs/gfs2/quota.c:1370)
[ 183.908520][ T8017] gfs2_sync_fs (fs/gfs2/super.c:670)
[ 183.909069][ T8017] sync_filesystem (fs/sync.c:57)
[ 183.909654][ T8017] generic_shutdown_super (fs/super.c:626)
[ 183.910287][ T8017] kill_block_super (fs/super.c:1681)
[ 183.910884][ T8017] gfs2_kill_sb (fs/gfs2/ops_fstype.c:1805)
[ 183.911456][ T8017] deactivate_locked_super (fs/super.c:479)
[ 183.912122][ T8017] deactivate_super (fs/super.c:512)
[ 183.912689][ T8017] cleanup_mnt (fs/namespace.c:144 fs/namespace.c:1268)
[ 183.913278][ T8017] task_work_run (kernel/task_work.c:182 (discriminator 1))
[ 183.913911][ T8017] do_exit (kernel/exit.c:872)
[ 183.914470][ T8017] do_group_exit (kernel/exit.c:1001)
[ 183.915075][ T8017] __x64_sys_exit_group (kernel/exit.c:1029)
[ 183.915718][ T8017] do_syscall_64 (arch/x86/entry/common.c:52 arch/x86/entry/common.c:83)
[ 183.916333][ T8017] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:129)
[ 183.917111][ T8017]
[ 183.917424][ T8017] Freed by task 8017:
[ 183.917973][ T8017] kasan_save_stack (mm/kasan/common.c:48)
[ 183.918609][ T8017] kasan_save_track (./arch/x86/include/asm/current.h:42 mm/kasan/common.c:60 mm/kasan/common.c:70)
[ 183.919225][ T8017] kasan_save_free_info (mm/kasan/generic.c:637)
[ 183.919857][ T8017] poison_slab_object (mm/kasan/common.c:241 mm/kasan/common.c:212)
[ 183.920526][ T8017] __kasan_slab_free (mm/kasan/common.c:257)
[ 183.921176][ T8017] kmem_cache_free (mm/slub.c:4299 mm/slub.c:4363)
[ 183.921805][ T8017] gfs2_log_flush (./include/linux/list.h:373 fs/gfs2/log.c:1023 fs/gfs2/log.c:1167)
[ 183.922453][ T8017] do_sync (fs/gfs2/quota.c:1012)
[ 183.922986][ T8017] gfs2_quota_sync (fs/gfs2/quota.c:1370)
[ 183.923613][ T8017] gfs2_sync_fs (fs/gfs2/super.c:670)
[ 183.924185][ T8017] sync_filesystem (fs/sync.c:57)
[ 183.924822][ T8017] generic_shutdown_super (fs/super.c:626)
[ 183.925476][ T8017] kill_block_super (fs/super.c:1681)
[ 183.926128][ T8017] gfs2_kill_sb (fs/gfs2/ops_fstype.c:1805)
[ 183.926733][ T8017] deactivate_locked_super (fs/super.c:479)
[ 183.927436][ T8017] deactivate_super (fs/super.c:512)
[ 183.928045][ T8017] cleanup_mnt (fs/namespace.c:144 fs/namespace.c:1268)
[ 183.928614][ T8017] task_work_run (kernel/task_work.c:182 (discriminator 1))
[ 183.929223][ T8017] do_exit (kernel/exit.c:872)
[ 183.929781][ T8017] do_group_exit (kernel/exit.c:1001)
[ 183.930395][ T8017] __x64_sys_exit_group (kernel/exit.c:1029)
[ 183.931062][ T8017] do_syscall_64 (arch/x86/entry/common.c:52 arch/x86/entry/common.c:83)
[ 183.931653][ T8017] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:129)
[ 183.932432][ T8017]
[ 183.932730][ T8017] The buggy address belongs to the object at ffff88801bc1d1e0
[ 183.932730][ T8017] which belongs to the cache gfs2_bufdata of size 80
[ 183.934282][ T8017] The buggy address is located 24 bytes inside of
[ 183.934282][ T8017] freed 80-byte region [ffff88801bc1d1e0, ffff88801bc1d230)
[ 183.935766][ T8017]
[ 183.936023][ T8017] The buggy address belongs to the physical page:
[ 183.936706][ T8017] page:ffffea00006f0740 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1bc1d
[ 183.937775][ T8017] flags: 0xfff00000000800(slab|node=0|zone=1|lastcpupid=0x7ff)
[ 183.938618][ T8017] page_type: 0xffffffff()
[ 183.939104][ T8017] raw: 00fff00000000800 ffff888042a7cc80 dead000000000122 0000000000000000
[ 183.940035][ T8017] raw: 0000000000000000 0000000080190019 00000001ffffffff 0000000000000000
[ 183.940969][ T8017] page dumped because: kasan: bad access detected
[ 183.941685][ T8017] page_owner tracks the page as allocated
[ 183.942305][ T8017] page last allocated via order 0, migratetype Unmovable, gfp_mask 0x12c40(GFP_NOFS|__GFP_NOWARN|__GFP_NORETRY), pid 8017, tgid 8017 (poc), ts 178749233499, free_ts 178526211051
[ 183.944143][ T8017] post_alloc_hook (./include/linux/page_owner.h:31 mm/page_alloc.c:1533)
[ 183.944662][ T8017] get_page_from_freelist (mm/page_alloc.c:1542 mm/page_alloc.c:3311)
[ 183.945275][ T8017] __alloc_pages (mm/page_alloc.c:4568)
[ 183.946119][ T8017] new_slab (mm/slub.c:2191 mm/slub.c:2354 mm/slub.c:2407)
[ 183.946600][ T8017] ___slab_alloc (mm/slub.c:3541 (discriminator 3))
[ 183.947112][ T8017] __slab_alloc.constprop.0 (mm/slub.c:3625)
[ 183.947690][ T8017] kmem_cache_alloc (mm/slub.c:3678 mm/slub.c:3850 mm/slub.c:3867)
[ 183.948219][ T8017] gfs2_trans_add_meta (fs/gfs2/trans.c:169 fs/gfs2/trans.c:251)
[ 183.948775][ T8017] gfs2_alloc_blocks (fs/gfs2/rgrp.c:2240 fs/gfs2/rgrp.c:2449)
[ 183.949318][ T8017] gfs2_unstuff_dinode (fs/gfs2/bmap.c:108 fs/gfs2/bmap.c:166)
[ 183.949943][ T8017] gfs2_adjust_quota (fs/gfs2/quota.c:880)
[ 183.950493][ T8017] do_sync (fs/gfs2/quota.c:992)
[ 183.950955][ T8017] gfs2_quota_sync (fs/gfs2/quota.c:1370)
[ 183.951505][ T8017] gfs2_sync_fs (fs/gfs2/super.c:670)
[ 183.951997][ T8017] sync_filesystem (fs/sync.c:57)
[ 183.952522][ T8017] generic_shutdown_super (fs/super.c:626)
[ 183.953088][ T8017] page last free pid 777 tgid 777 stack trace:
[ 183.953801][ T8017] free_unref_page_prepare (./include/linux/page_owner.h:24 mm/page_alloc.c:1140 mm/page_alloc.c:2346)
[ 183.954429][ T8017] free_unref_page_list (mm/page_alloc.c:2532)
[ 183.954999][ T8017] release_pages (mm/swap.c:961)
[ 183.955537][ T8017] __folio_batch_release (./include/linux/pagevec.h:48 mm/swap.c:1063)
[ 183.956126][ T8017] truncate_inode_pages_range (mm/truncate.c:363)
[ 183.956791][ T8017] inode_go_inval (fs/gfs2/glops.c:365)
[ 183.957311][ T8017] do_xmote (./include/linux/instrumented.h:82 ./include/asm-generic/bitops/instrumented-atomic.h:41 fs/gfs2/glock.c:751)
[ 183.957793][ T8017] run_queue (fs/gfs2/glock.c:875)
[ 183.958266][ T8017] glock_work_func (fs/gfs2/glock.c:1107)
[ 183.958791][ T8017] process_one_work (kernel/workqueue.c:2638)
[ 183.959343][ T8017] worker_thread (kernel/workqueue.c:2700 kernel/workqueue.c:2787)
[ 183.959865][ T8017] kthread (kernel/kthread.c:388)
[ 183.960329][ T8017] ret_from_fork (arch/x86/kernel/process.c:153)
[ 183.960861][ T8017] ret_from_fork_asm (arch/x86/entry/entry_64.S:250)
[ 183.961381][ T8017]
[ 183.961652][ T8017] Memory state around the buggy address:
[ 183.962244][ T8017] ffff88801bc1d080: fc fc fc fc fa fb fb fb fb fb fb fb fb fb fc fc
[ 183.963124][ T8017] ffff88801bc1d100: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
[ 183.964014][ T8017] >ffff88801bc1d180: fb fb fc fc fc fc fc fc fc fc fc fc fa fb fb fb
[ 183.964856][ T8017] ^
[ 183.965733][ T8017] ffff88801bc1d200: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc
[ 183.966612][ T8017] ffff88801bc1d280: fa fb fb fb fb fb fb fb fb fb fc fc fc fc fc fc
[ 183.967504][ T8017] ==================================================================
Analyze of the issue:
The gfs_bufdata(fs/gfs2/trans.c:163) function will allocate memory(line:168), and store the memory address in the bh->private(line:174).
static struct gfs2_bufdata *gfs2_alloc_bufdata(struct gfs2_glock *gl,
struct buffer_head *bh)
{
struct gfs2_bufdata *bd;
bd = kmem_cache_zalloc(gfs2_bufdata_cachep, GFP_NOFS | __GFP_NOFAIL); // line:168
// ...
bh->b_private = bd; // line:174
return bd;
}
The trans_drain(fs/gfs2/log.c:1006) function will free memory(line 1028). However, the code does not set bh->b_private to be NULL after free memory. So, the bh->b_private is point to a memory address that has already been freed.
static void trans_drain(struct gfs2_trans *tr)
{
struct gfs2_bufdata *bd;
// ...
head = &tr->tr_databuf;
while (!list_empty(head)) {
bd = list_first_entry(head, struct gfs2_bufdata, bd_list); // line: 1024
// ...
kmem_cache_free(gfs2_bufdata_cachep, bd); // line: 1028
}
}
The gfs2_discard(fs/gfs2/aops.c:608) function will get the memory address from bh->b_private, and the memory address will be not NULL, because the trans_drain function did not set bh->b_private to be NULL after free memory. So, the code will pass the check in the line 616. And in the line 617, the code will read the memory which has been freed.
static void gfs2_discard(struct gfs2_sbd *sdp, struct buffer_head *bh)
{
struct gfs2_bufdata *bd;
bd = bh->b_private; // line:615
if (bd) { // line: 616
if (!list_empty(&bd->bd_list) && !buffer_pinned(bh)) // line: 617
list_del_init(&bd->bd_list);
// ...
}
// ...
}
Thank you for taking the time to read this email and we look forward to working with you further.
Ubisectech Sirius Team
Web: http://www.ubisectech.com
Email: [email protected]


Attachments:
poc.c (583.45 kB)