Skip to content
cvekernelbtrfsuse-after-free

Linux kernel btrfs use-after-free

3 min read

Overview

Mounting a crafted image twice can cause a use-after-free vulnerability in the rwsem_can_spin_on_owner function.

Target

Linux Kernel 5.3.11 btrfs filesystem.

Linux Version Availablity
5.0.21 True
5.3.11 True
5.4.0 mount fail

Bug type: Use After Free.

Reproduce

mkdir mnt
mount poc_2019_19318.img ./mnt
(1st mount crash, kernel BUG)
mount poc_2019_19318.img ./mnt
(2nd mount crash, use-after-free)

Root cause

kernel/locking/rwsem.c/673 (link)

static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem,
					   unsigned long nonspinnable)
{
	struct task_struct *owner;
	unsigned long flags;
	bool ret = true;
 
	BUILD_BUG_ON(!(RWSEM_OWNER_UNKNOWN & RWSEM_NONSPINNABLE));
 
	if (need_resched()) {
		lockevent_inc(rwsem_opt_fail);
		return false;
	}
 
	preempt_disable();
	rcu_read_lock();
[1]	owner = rwsem_owner_flags(sem, &flags);
	/*
	 * Don't check the read-owner as the entry may be stale.
	 */
	if ((flags & nonspinnable) ||
[2]	    (owner && !(flags & RWSEM_READER_OWNED) && !owner_on_cpu(owner)))
		ret = false;
	rcu_read_unlock();
	preempt_enable();
 
	lockevent_cond_inc(rwsem_opt_fail, !ret);
	return ret;
}

rwsem_owner_flags returns an already freed pointer [1]. This causes a use-after-free in [2].

Debugger / KASAN

Debugger view — rwsem_owner_flags(sem, &flags) returns an already freed task_struct:

─────────────────────────────────────────────────────────── arguments ────
__asan_load4 (
   long unsigned int var_0 = 0xffff88806cf519b8 → 0x0000000000000000 → 0x0000000000000000
)
───────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff8111b0df → owner_on_cpu(owner=<optimized out>)
[#1] 0xffffffff8111b0df → rwsem_can_spin_on_owner(...)
[#2] 0xffffffff8111b0df → rwsem_down_write_slowpath(sem=0xffff88806a25e670, state=<optimized out>)
[#3] 0xffffffff8240a6be → __down_write(sem=<optimized out>)
[#4] 0xffffffff8240a6be → down_write(sem=0xffff88806a25e670)
[#5] 0xffffffff812f6f8a → grab_super(s=0xffff88806a25e600)
[#6] 0xffffffff812f821a → sget(...)
[#7] 0xffffffff815677a7 → btrfs_mount_root(...)
...

1st mount — kernel BUG (trimmed to the relevant frames):

[  118.434418] ------------[ cut here ]------------
[  118.437045] WARNING: CPU: 0 PID: 195 at fs/btrfs/extent-tree.c:874 btrfs_lookup_extent_info+0x5c4/0x640
...
[  118.480945] ------------[ cut here ]------------
[  118.481284] kernel BUG at fs/btrfs/extent-tree.c:6521!
[  118.485682] invalid opcode: 0000 [#1] SMP KASAN NOPTI
[  118.486243] RIP: 0010:walk_down_proc+0x416/0x440
[  118.486243] Call Trace:
[  118.486243]  walk_down_tree+0xe0/0x1f0
[  118.486243]  btrfs_drop_snapshot+0x730/0xdc0
[  118.486243]  clean_dirty_subvols+0x17c/0x1c0
[  118.486243]  btrfs_recover_relocation+0x640/0x6d0
[  118.486243]  open_ctree+0x2f65/0x393d
[  118.486243]  btrfs_mount_root+0x8c3/0x9e0
[  118.486243]  do_mount+0x96d/0xd10
[  118.486243]  ksys_mount+0x79/0xc0
[  118.486243]  __x64_sys_mount+0x5d/0x70
[  118.486243]  do_syscall_64+0x5e/0x190
[  118.486243]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  118.522863] ---[ end trace 6e1e05ca401f9abb ]---
Segmentation fault

2nd mount — use-after-free (trimmed to the relevant frames):

[  144.683854] ==================================================================
[  144.684623] BUG: KASAN: use-after-free in rwsem_down_write_slowpath+0x724/0x8d0
[  144.684623] Read of size 4 at addr ffff88806c57bff8 by task mount/227
[  144.684623] CPU: 1 PID: 227 Comm: mount Tainted: G      D W         5.3.11 #1
[  144.684623] Call Trace:
[  144.684623]  dump_stack+0x76/0xab
[  144.684623]  kasan_report+0xe/0x20
[  144.684623]  rwsem_down_write_slowpath+0x724/0x8d0
[  144.684623]  down_write+0x10e/0x120
[  144.684623]  grab_super+0x8a/0x160
[  144.684623]  sget+0xda/0x230
[  144.684623]  btrfs_mount_root+0x557/0x9e0
[  144.684623]  do_mount+0x96d/0xd10
[  144.684623]  ksys_mount+0x79/0xc0
[  144.684623]  __x64_sys_mount+0x5d/0x70
[  144.684623]  do_syscall_64+0x5e/0x190
[  144.684623]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  144.684623] Allocated by task 154:
[  144.684623]  kmem_cache_alloc_node+0xe0/0x1f0
[  144.684623]  copy_process+0xe19/0x2e00
[  144.684623]  _do_fork+0xec/0x4d0
[  144.684623]  __x64_sys_clone+0xfd/0x150
[  144.684623] Freed by task 0:
[  144.684623]  __kasan_slab_free+0x132/0x180
[  144.684623]  kmem_cache_free+0x75/0x280
[  144.684623]  rcu_core+0x2be/0xbe0
[  144.684623]  __do_softirq+0x11b/0x3b3
[  144.684623] The buggy address belongs to the object at ffff88806c57bfc0
[  144.684623]  which belongs to the cache task_struct of size 3136
[  144.684623] ==================================================================

Details

CVSS 4.4 (CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H), CWE-416, published 2019-11-28, last modified 2024-11-21.

Attribution

Found as part of the Best of the Best (BoB) 8th bobfuzzer team project — a five-person team that ported the JANUS file-system fuzzer. This is team work, not sole authorship.

References