Skip to content
cvekernelbtrfsout-of-bounds

Linux kernel btrfs slab out-of-bounds write

7 min read

Overview

some operation(with crafted btrfs filesystem image) can cause out of bounds write in __btrfs_map_block

Target

Linux kernel btrfs FileSystem

Linux Version Availablity
5.0.21 True

Reproduce

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

Root cause

static int __btrfs_map_block(struct btrfs_fs_info *fs_info,
			     enum btrfs_map_op op,
			     u64 logical, u64 *length,
			     struct btrfs_bio **bbio_ret,
			     int mirror_num, int need_raid_map)
{
	...
 
	/* build raid_map */
	if (map->type & BTRFS_BLOCK_GROUP_RAID56_MASK && need_raid_map &&
	    (need_full_stripe(op) || mirror_num > 1)) {
		u64 tmp;
		unsigned rot;
 
		bbio->raid_map = (u64 *)((void *)bbio->stripes +
				 sizeof(struct btrfs_bio_stripe) *
				 num_alloc_stripes +
				 sizeof(int) * tgtdev_indexes);
 
		/* Work out the disk rotation on this stripe-set */
		div_u64_rem(stripe_nr, num_stripes, &rot);
 
		/* Fill in the logical address of each stripe */
		tmp = stripe_nr * nr_data_stripes(map);
[1]		for (i = 0; i < nr_data_stripes(map); i++)
			bbio->raid_map[(i+rot) % num_stripes] =
				em->start + (tmp + i) * map->stripe_len;
 
[2]		bbio->raid_map[(i+rot) % map->num_stripes] = RAID5_P_STRIPE;
		...

In line [1], nr_data_stripes(map) returns 1.

when [(i+rot) % map->num_stripes] is 1 in [2], it occurs out of bounds write

Debugger / KASAN

Debugger view:

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax   : 0x0000000000000000  →  0x0000000000000000
$rbx   : 0x0000000000000001  →  0x0000000000000001
$rcx   : 0xffffffff816a1b16  →  0x005d894824048b48  →  0x005d894824048b48
$rdx   : 0x0000000000000001  →  0x0000000000000001
$rsp   : 0xffff88806353f470  →  0xffff888066d49000  →  0x0000000000000001  →  0x0000000000000001
$rbp   : 0xffff888066d49080  →  0xa64000ac00000752  →  0xa64000ac00000752
$rsi   : 0x0000000000000002  →  0x0000000000000002
$rdi   : 0xffff888066d49078  →  0x0000000001400000  →  0x0000000001400000
$rip   : 0xffffffff816a1b7a  →  0xffc1921ee8ef8948  →  0xffc1921ee8ef8948
$r8    : 0xffffed100cda9201  →  0x0000000000000000  →  0x0000000000000000
$r9    : 0xffffed100cda9201  →  0x0000000000000000  →  0x0000000000000000
$r10   : 0x0000000000000001  →  0x0000000000000001
$r11   : 0xffffed100cda9200  →  0x0000000000000000  →  0x0000000000000000
$r12   : 0x0000000000000000  →  0x0000000000000000
$r13   : 0x0000000000000001  →  0x0000000000000001
$r14   : 0x0000000000000001  →  0x0000000000000001
$r15   : 0x0000000000000000  →  0x0000000000000000
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0010 $ss: 0x0018 $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0xffff88806353f470│+0x0000: 0xffff888066d49000  →  0x0000000000000001  →  0x0000000000000001	 ← $rsp
0xffff88806353f478│+0x0008: 0xffff88806ca9fe10  →  0x0000000000010000  →  0x0000000000010000
0xffff88806353f480│+0x0010: 0x0000000000000000  →  0x0000000000000000
0xffff88806353f488│+0x0018: 0xffff888067908018  →  0x0000000001400000  →  0x0000000001400000
0xffff88806353f490│+0x0020: 0x0000000000000000  →  0x0000000000000000
0xffff88806353f498│+0x0028: 0x0000000000000001  →  0x0000000000000001
0xffff88806353f4a0│+0x0030: 0xffff888066d49058  →  0xffff888066d49078  →  0x0000000001400000  →  0x0000000001400000
0xffff88806353f4a8│+0x0038: 0x0000000000000000  →  0x0000000000000000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0xffffffff816a1b71 <__btrfs_map_block+1233> div    esi
   0xffffffff816a1b73 <__btrfs_map_block+1235> mov    edx, edx
   0xffffffff816a1b75 <__btrfs_map_block+1237> lea    rbp, [rbp+rdx*8+0x0]
 → 0xffffffff816a1b7a <__btrfs_map_block+1242> mov    rdi, rbp
   0xffffffff816a1b7d <__btrfs_map_block+1245> call   0xffffffff812bada0 <__asan_store8>
   0xffffffff816a1b82 <__btrfs_map_block+1250> mov    rax, QWORD PTR [rsp+0x48]
   0xffffffff816a1b87 <__btrfs_map_block+1255> mov    QWORD PTR [rbp+0x0], 0xfffffffffffffffe
   0xffffffff816a1b8f <__btrfs_map_block+1263> mov    rax, QWORD PTR [rax]
   0xffffffff816a1b92 <__btrfs_map_block+1266> test   ah, 0x1
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── source:fs/btrfs/volumes.c+6247 ────
   6242	 		tmp = stripe_nr * nr_data_stripes(map);
   6243	 		for (i = 0; i < nr_data_stripes(map); i++)
   6244	 			bbio->raid_map[(i+rot) % num_stripes] =
   6245	 				em->start + (tmp + i) * map->stripe_len;
   6246
[1] → 6247	 		bbio->raid_map[(i+rot) % map->num_stripes] = RAID5_P_STRIPE;
   6248	 		if (map->type & BTRFS_BLOCK_GROUP_RAID6)
   6249	 			bbio->raid_map[(i+rot+1) % num_stripes] =
   6250	 				RAID6_Q_STRIPE;
   6251	 	}
   6252
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, stopped 0xffffffff816a1b7a in __btrfs_map_block (), reason: SINGLE STEP
[#1] Id 2, stopped 0xffffffff812bad33 in memory_is_poisoned_2_4_8 (), reason: SINGLE STEP
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff816a1b7a → __btrfs_map_block(fs_info=<optimized out>, op=<optimized out>, logical=<optimized out>, length=<optimized out>, bbio_ret=<optimized out>, mirror_num=<optimized out>, need_raid_map=0x1)
[#1] 0xffffffff816a37cf → btrfs_map_bio(fs_info=0xffff88806a245500, bio=0xffff888065d064f0, mirror_num=0x0, async_submit=<optimized out>)
[#2] 0xffffffff8164e592 → btree_submit_bio_hook(private_data=<optimized out>, bio=0xffff888065d064f0, mirror_num=0x0, bio_flags=<optimized out>, bio_offset=0x1400000)
[#3] 0xffffffff8168ad59 → submit_one_bio(bio=0xffff888065d064f0, mirror_num=0x0, bio_flags=0x0)
[#4] 0xffffffff8168b2aa → flush_write_bio(epd=<optimized out>)
[#5] 0xffffffff81697014 → btree_write_cache_pages(mapping=<optimized out>, wbc=0xffff88806353fa48)
[#6] 0xffffffff8123c54d → do_writepages(mapping=0xffff8880683c4388, wbc=0xffff88806353fa48)
[#7] 0xffffffff81224eb8 → __filemap_fdatawrite_range(mapping=0xffff8880683c4388, start=<optimized out>, end=<optimized out>, sync_mode=<optimized out>)
[#8] 0xffffffff81224fda → filemap_fdatawrite_range(mapping=<optimized out>, start=<optimized out>, end=<optimized out>)
[#9] 0xffffffff81658f1d → btrfs_write_marked_extents(fs_info=0xffff88806a245500, dirty_pages=0xffff8880692a8cb8, mark=0x1)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

KASAN log (the trailing kernel BUG panic is truncated):

[  393.376653] ==================================================================
[  393.376653] BUG: KASAN: slab-out-of-bounds in __btrfs_map_block+0x4e2/0x1980
[  393.376653] Write of size 8 at addr ffff888066d49080 by task 263/1874
[  393.376653]
[  393.376653] CPU: 0 PID: 1874 Comm: 263 Not tainted 5.0.21 #2
[  393.376653] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[  393.376653] Call Trace:
[  393.376653]  dump_stack+0x5b/0x8b
[  393.376653]  print_address_description+0x70/0x280
[  393.376653]  ? __btrfs_map_block+0x4e2/0x1980
[  393.376653]  kasan_report+0x13a/0x19b
[  393.376653]  ? __btrfs_map_block+0x4e2/0x1980
[  393.376653]  ? __btrfs_map_block+0x4e2/0x1980
[  393.376653]  __btrfs_map_block+0x4e2/0x1980
[  393.376653]  ? btrfs_put_bbio+0x20/0x20
[  393.376653]  ? btrfs_bio_counter_inc_blocked+0xa0/0x160
[  393.376653]  ? btrfs_bio_counter_sub+0x70/0x70
[  393.376653]  ? bvec_alloc+0x8b/0x160
[  393.376653]  ? btrfs_wq_submit_bio+0x120/0x120
[  393.376653]  btrfs_map_bio+0x12f/0x650
[  393.376653]  ? btrfs_rmap_block+0x3a0/0x3a0
[  393.376653]  ? btree_csum_one_bio.isra.42+0x145/0x160
[  393.376653]  ? btrfs_wq_submit_bio+0x120/0x120
[  393.376653]  btree_submit_bio_hook+0xe2/0x130
[  393.376653]  submit_one_bio+0xc9/0x100
[  393.376653]  flush_write_bio.isra.41+0x1a/0x40
[  393.376653]  btree_write_cache_pages+0x394/0x530
[  393.376653]  ? write_one_eb+0x3a0/0x3a0
[  393.376653]  ? __sched_text_start+0x8/0x8
[  393.376653]  ? kasan_unpoison_shadow+0x31/0x40
[  393.376653]  ? rb_next+0x4f/0x80
[  393.376653]  ? merge_state.part.45+0x7d/0x250
[  393.376653]  ? __kasan_slab_free+0x147/0x180
[  393.376653]  ? convert_extent_bit+0x4f8/0x6b0
[  393.376653]  ? kmem_cache_free+0x70/0x190
[  393.376653]  ? convert_extent_bit+0x4f8/0x6b0
[  393.376653]  do_writepages+0x2d/0xa0
[  393.376653]  __filemap_fdatawrite_range+0xf8/0x160
[  393.376653]  ? delete_from_page_cache_batch+0x4a0/0x4a0
[  393.376653]  ? btrfs_commit_transaction+0x7da/0xf90
[  393.376653]  ? find_first_extent_bit+0xe6/0x1b0
[  393.376653]  btrfs_write_marked_extents+0x24d/0x260
[  393.376653]  ? btrfs_should_end_transaction+0x80/0x80
[  393.376653]  ? _raw_write_lock_irqsave+0xf0/0xf0
[  393.376653]  ? _raw_read_lock_irq+0x30/0x30
[  393.376653]  ? __wake_up_common+0x4a/0x1d0
[  393.376653]  btrfs_write_and_wait_transaction.isra.21+0xab/0x120
[  393.376653]  ? btrfs_wait_extents+0x40/0x40
[  393.376653]  ? btrfs_commit_transaction+0xbcb/0xf90
[  393.376653]  btrfs_commit_transaction+0xbcb/0xf90
[  393.376653]  ? btrfs_apply_pending_changes+0x80/0x80
[  393.376653]  ? up_write+0xd/0x30
[  393.376653]  btrfs_sync_file+0x5b3/0x600
[  393.376653]  ? start_ordered_ops+0x120/0x120
[  393.376653]  ? vfs_read+0x122/0x190
[  393.376653]  ? ksys_read+0x131/0x160
[  393.376653]  ? kernel_write+0x90/0x90
[  393.376653]  ? vfs_fsync_range+0x81/0x100
[  393.376653]  do_fsync+0x33/0x60
[  393.376653]  __x64_sys_fsync+0x18/0x20
[  393.376653]  do_syscall_64+0x5e/0x150
[  393.376653]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  393.376653] RIP: 0033:0x7f1fb191e469
[  393.376653] Code: 00 f3 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ff 49 2b 00 f7 d8 64 89 01 48
[  393.376653] RSP: 002b:00007ffe703b9708 EFLAGS: 00000217 ORIG_RAX: 000000000000004a
[  393.376653] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f1fb191e469
[  393.376653] RDX: 00007f1fb191e469 RSI: 0000000000001c52 RDI: 0000000000000003
[  393.376653] RBP: 00007ffe703bb7b0 R08: 00007ffe703bb898 R09: 00007ffe703bb898
[  393.376653] R10: 00007ffe703bb898 R11: 0000000000000217 R12: 000055ebba9005f0
[  393.376653] R13: 00007ffe703bb890 R14: 0000000000000000 R15: 0000000000000000
[  393.376653]
[  393.376653] Allocated by task 1874:
[  393.376653]  __kasan_kmalloc+0xd5/0xf0
[  393.376653]  alloc_btrfs_bio+0x1b/0x60
[  393.376653]  __btrfs_map_block+0xe9a/0x1980
[  393.376653]  btrfs_map_bio+0x12f/0x650
[  393.376653]  btree_submit_bio_hook+0xe2/0x130
[  393.376653]  submit_one_bio+0xc9/0x100
[  393.376653]  flush_write_bio.isra.41+0x1a/0x40
[  393.376653]  btree_write_cache_pages+0x394/0x530
[  393.376653]  do_writepages+0x2d/0xa0
[  393.376653]  __filemap_fdatawrite_range+0xf8/0x160
[  393.376653]  btrfs_write_marked_extents+0x24d/0x260
[  393.376653]  btrfs_write_and_wait_transaction.isra.21+0xab/0x120
[  393.376653]  btrfs_commit_transaction+0xbcb/0xf90
[  393.376653]  btrfs_sync_file+0x5b3/0x600
[  393.376653]  do_fsync+0x33/0x60
[  393.376653]  __x64_sys_fsync+0x18/0x20
[  393.376653]  do_syscall_64+0x5e/0x150
[  393.376653]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  393.376653]
[  393.376653] Freed by task 1835:
[  393.376653]  __kasan_slab_free+0x132/0x180
[  393.376653]  kfree+0x8c/0x1a0
[  393.376653]  blk_rq_unmap_user+0x34/0x70
[  393.376653]  sg_io+0x466/0x680
[  393.376653]  scsi_cmd_ioctl+0x362/0x4e0
[  393.376653]  cdrom_ioctl+0x106/0x1419
[  393.376653]  sr_block_ioctl+0xc0/0xf0
[  393.376653]  blkdev_ioctl+0x8e7/0xdb0
[  393.376653]  block_ioctl+0x68/0x80
[  393.376653]  do_vfs_ioctl+0x139/0x8c0
[  393.376653]  ksys_ioctl+0x61/0x70
[  393.376653]  __x64_sys_ioctl+0x38/0x40
[  393.376653]  do_syscall_64+0x5e/0x150
[  393.376653]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  393.376653]
[  393.376653] The buggy address belongs to the object at ffff888066d49000
[  393.376653]  which belongs to the cache kmalloc-128 of size 128
[  393.376653] The buggy address is located 0 bytes to the right of
[  393.376653]  128-byte region [ffff888066d49000, ffff888066d49080)
[  393.376653] The buggy address belongs to the page:
[  393.376653] page:ffffea00019b5240 count:1 mapcount:0 mapping:ffff88806d001640 index:0x0
[  393.376653] flags: 0x100000000000200(slab)
[  393.376653] raw: 0100000000000200 ffffea0001a98040 0000001700000017 ffff88806d001640
[  393.376653] raw: 0000000000000000 0000000080150015 00000001ffffffff 0000000000000000
[  393.376653] page dumped because: kasan: bad access detected
[  393.376653]
[  393.376653] Memory state around the buggy address:
[  393.376653]  ffff888066d48f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[  393.376653]  ffff888066d49000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[  393.376653] >ffff888066d49080: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb
[  393.376653]                    ^
[  393.376653]  ffff888066d49100: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[  393.376653]  ffff888066d49180: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[  393.376653] ==================================================================
[  393.376653] Disabling lock debugging due to kernel taint
[  393.754098] ------------[ cut here ]------------
[  393.754098] kernel BUG at fs/btrfs/raid56.c:522!
...

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-12-17, 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