Skip to content
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
CVE/CVE-2019-19813/
CVE/CVE-2019-19813/

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 

CVE-2019-19813

Target

Linux kernel btrfs FileSystem

Linux Version Availablity
5.0.21 True

Bug Type

Use After Free

Abstract

syncfs syscall after some operation(with crafted image) can cause use-after-free vulnerability in mutex_can_spin_on_owner

Reproduce

gcc -o poc poc_2019_19813.c
mkdir mnt
mount poc_2019_19813.img ./mnt
cp poc ./mnt/
cd mnt
./poc
cd ..
sync

Details

Debug View

─────────────────────────────────────────────────────────── 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

Bug Causes

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

KASAN logs

[  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] ==================================================================

Conclusion

in btrfs_insert_delayed_items function, mutex_lock(&node->mutex) calls mutex_can_spin_on_owner, with freed lock->owner

Discoverer

Team bobfuzzer

Acknowledgments

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.