Skip to content
cvekernelbtrfsnull-pointer-dereference

Linux kernel btrfs root node NULL pointer dereference

3 min read

Overview

Mounting a crafted image can cause a NULL-pointer dereference. It can be triggered not only locally (mounting a btrfs image in a local shell) but also remotely (mounting a corrupted USB or other storage carrying a crafted btrfs image).

Target

Linux Kernel 5.0.21 btrfs filesystem. The bug affects btrfs_root_node in fs/btrfs/ctree.c in the Linux kernel through 5.3.12.

Bug type: NULL-Pointer-Dereference.

Reproduce

mkdir ./mount
mount poc_2019_19036.img ./mnt

Root cause

struct extent_buffer *btrfs_root_node(struct btrfs_root *root)
{
	struct extent_buffer *eb;
 
	while (1) {
		rcu_read_lock();
[1]		eb = rcu_dereference(root->node);
 
		/*
		 * RCU really hurts here, we could free up the root node because
		 * it was COWed but we may not get the new root node yet so do
		 * the inc_not_zero dance and if it doesn't work then
		 * synchronize_rcu and try again.
		 */
[2]		if (atomic_inc_not_zero(&eb->refs)) {
			rcu_read_aunlock();
			break;
		}
		rcu_read_unlock();
		synchronize_rcu();
	}
	return eb;
}

In [1], rcu_dereference(root->node) returns 0. It is then used in [2] (eb is 0, so eb->refs equals 0x00 + 0x24).

Debugger / KASAN

Debugger view — $r15 (struct extent_buffer *eb, the return value of rcu_dereference(root->node)) appears to be 0:

───────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff81c8c6a4 → atomic_fetch_add_unless(...)
[#1] 0xffffffff81c8c6a4 → atomic_add_unless(...)
[#2] 0xffffffff81c8c6a4 → btrfs_root_node(root=<optimized out>)
[#3] 0xffffffff81c8c925 → btrfs_read_lock_root_node(root=0xffff888069bcc400)
[#4] 0xffffffff81c9be94 → btrfs_search_slot_get_root(...)
[#5] 0xffffffff81c9be94 → btrfs_search_slot(...)
[#6] 0xffffffff81cf73ae → btrfs_find_root(...)
[#7] 0xffffffff81d19e54 → btrfs_read_tree_root(...)
[#8] 0xffffffff81d1a049 → btrfs_read_fs_root(...)
[#9] 0xffffffff81d1a338 → btrfs_get_fs_root(...)

KASAN log (trimmed to the relevant frames):

[  166.370019] ==================================================================
[  166.370195] BUG: KASAN: null-ptr-deref in btrfs_root_node+0x119/0x300
[  166.370195] Read of size 4 at addr 0000000000000024 by task kworker/u4:4/174
[  166.370195] CPU: 0 PID: 174 Comm: kworker/u4:4 Not tainted 5.0.21 #1
[  166.370195] Workqueue: btrfs-endio-meta btrfs_endio_meta_helper
[  166.370195] Call Trace:
[  166.370195]  dump_stack+0xae/0x14b
[  166.370195]  kasan_report+0x171/0x18d
[  166.370195]  btrfs_root_node+0x119/0x300
[  166.370195]  btrfs_read_lock_root_node+0x35/0x60
[  166.370195]  btrfs_search_slot+0x10c4/0x2190
[  166.370195]  btrfs_find_root+0xbe/0xb20
[  166.370195]  btrfs_read_tree_root+0x144/0x330
[  166.370195]  btrfs_read_fs_root+0x9/0xb0
[  166.370195]  btrfs_get_fs_root+0x248/0x810
[  166.370195]  check_leaf+0x31e/0x17e0
[  166.370195]  btree_readpage_end_io_hook+0x5a2/0x7d0
[  166.370195]  end_bio_extent_readpage+0x525/0x10e0
[  166.370195]  bio_endio+0x36a/0x680
[  166.370195]  normal_work_helper+0x24a/0xf30
[  166.370195]  process_one_work+0x90a/0x1690
[  166.370195]  worker_thread+0x191/0x1200
[  166.370195]  kthread+0x2e4/0x3a0
[  166.370195]  ret_from_fork+0x35/0x40
[  166.370195] ==================================================================
...
[  166.414228] RIP: 0010:btrfs_root_node+0x12a/0x300
[  166.414228] R13: 0000000000000024 R14: dffffc0000000000 R15: 0000000000000000
[  166.453905] ---[ end trace 43259c89f26aad18 ]---

A crafted image can force rcu_dereference(root->node) to return 0, which can be dangerous in other functions too.

Details

CVSS 5.5 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H), CWE-476, published 2019-11-21, 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