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.