Skip to content
cvekernelbtrfsout-of-bounds-write

Linux kernel btrfs slab out-of-bounds write

3 min read

Overview

Mounting a crafted image can cause a slab-out-of-bounds write in index_rbio_pages. 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 btrfs filesystem.

Linux Version Availablity
5.0.21 True

Bug type: slab-out-of-bounds write.

Reproduce

mkdir mnt
mount poc_2019_19378.img ./mnt

Root cause

fs/btrfs/raid56.c:1163 (link)

static void index_rbio_pages(struct btrfs_raid_bio *rbio)
{
	struct bio *bio;
	u64 start;
	unsigned long stripe_offset;
	unsigned long page_index;
 
	spin_lock_irq(&rbio->bio_list_lock);
	bio_list_for_each(bio, &rbio->bio_list) {
		struct bio_vec bvec;
		struct bvec_iter iter;
		int i = 0;
 
		start = (u64)bio->bi_iter.bi_sector << 9;
		stripe_offset = start - rbio->bbio->raid_map[0];
[1]		page_index = stripe_offset >> PAGE_SHIFT;
 
		if (bio_flagged(bio, BIO_CLONED))
			bio->bi_iter = btrfs_io_bio(bio)->iter;
 
		bio_for_each_segment(bvec, bio, iter) {
[2]			rbio->bio_pages[page_index + i] = bvec.bv_page;
			i++;
		}
	}
	spin_unlock_irq(&rbio->bio_list_lock);
}

The local variable page_index becomes 0xc [1], so the rbio->bio_pages array takes a slab-out-of-bounds write in [2].

Debugger / KASAN

Debugger view — when the local variable i = 0 and page_index = 0xc, an out-of-bounds write occurs in the rbio->bio_pages array:

─────────────────────────────────────────────────────────── arguments ────
__asan_report_store8_noabort (
   long unsigned int var_0 = 0xffff88806b90ae38 → 0x0000000000000000 → 0x0000000000000000
)
───────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff81bc11d1 → index_rbio_pages(rbio=0xffff88806b90ac80)
[#1] 0xffffffff81bcba99 → raid56_rmw_stripe(rbio=<optimized out>)
[#2] 0xffffffff81bcba99 → rmw_work(work=0xffff88806b90acb0)
[#3] 0xffffffff81aecaaf → normal_work_helper(work=0xffff88806b90acb0)
[#4] 0xffffffff8115fed0 → process_one_work(...)
[#5] 0xffffffff8116184a → worker_thread(__worker=0xffff88806cfdf100)
[#6] 0xffffffff811712f9 → kthread(_create=<optimized out>)
[#7] 0xffffffff83800215 → ret_from_fork()

gef➤  p i
$24 = 0x0
gef➤  p page_index
$25 = 0xc

KASAN log (trimmed to the relevant frames):

[  158.837964] ==================================================================
[  158.837964] BUG: KASAN: slab-out-of-bounds in index_rbio_pages+0x516/0x800
[  158.837964] Write of size 8 at addr ffff88806b90ae38 by task kworker/u4:1/20
[  158.837964] CPU: 0 PID: 20 Comm: kworker/u4:1 Not tainted 5.0.21 #1
[  158.837964] Workqueue: btrfs-rmw btrfs_rmw_helper
[  158.837964] Call Trace:
[  158.837964]  dump_stack+0x5b/0x8b
[  158.837964]  print_address_description+0x70/0x280
[  158.837964]  kasan_report+0x13a/0x19b
[  158.837964]  index_rbio_pages+0x516/0x800
[  158.837964]  rmw_work+0xb9/0x650
[  158.837964]  normal_work_helper+0x1cf/0xa50
[  158.837964]  process_one_work+0x580/0x1210
[  158.837964]  worker_thread+0x8a/0xfc0
[  158.837964]  kthread+0x2a9/0x390
[  158.837964]  ret_from_fork+0x35/0x40
[  158.837964] Allocated by task 1863:
[  158.837964]  __kasan_kmalloc+0xd5/0xf0
[  158.837964]  alloc_rbio+0xb3/0x890
[  158.837964]  raid56_parity_write+0x22/0x440
[  158.837964]  btrfs_map_bio+0x982/0xd50
[  158.837964]  btree_submit_bio_hook+0x246/0x2a0
[  158.837964]  submit_one_bio+0x1be/0x320
[  158.837964]  btrfs_sync_file+0x817/0x9e0
[  158.837964]  do_fsync+0x33/0x60
[  158.837964]  __x64_sys_fsync+0x2a/0x40
[  158.837964]  do_syscall_64+0x8c/0x280
[  158.837964]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  158.837964] The buggy address belongs to the object at ffff88806b90ac80
[  158.837964]  which belongs to the cache kmalloc-512 of size 512
[  158.837964] The buggy address is located 440 bytes inside of
[  158.837964]  512-byte region [ffff88806b90ac80, ffff88806b90ae80)
[  158.837964] ==================================================================

Details

CVSS 7.8 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H), CWE-787, published 2019-11-29, last modified 2026-05-28.

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