CVE-2019-19378
Target
Linux Kernel btrfs FileSystem
| Linux Version | Availablity |
|---|---|
| 5.0.21 | True |
Bug Type
slab-out-of-bounds write
Abstract
mounting crafted image can cause slab-out-of-bounds write in index_rbio_pages
it can be not only local(mount btrfs image in local shell), but also remote(mount corrupted(with crafted btrfs image) USB or other storage)
Reproduce
mkdir mnt
mount poc_2019_19378.img ./mntDetails
Debug view
────────────────────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x0000000000000000 → 0x0000000000000000
$rbx : 0x0000000000001000 → 0x0000000000001000
$rcx : 0xffff88806b90ae38 → 0x0000000000000000 → 0x0000000000000000
$rdx : 0xffffea0001b04e80 → 0x010000000000a016 → 0x010000000000a016
$rsp : 0xffff88806c62fc20 → 0xffff88806b90ac88 → 0xffff888069bd5700 → 0x0000000200000001 → 0x0000000200000001
$rbp : 0x0000000000001000 → 0x0000000000001000
$rsi : 0x0000000000000000 → 0x0000000000000000
$rdi : 0xffff88806b90ae38 → 0x0000000000000000 → 0x0000000000000000
$rip : 0xffffffff81bc11d1 → 0x5c8b4cff957edae8 → 0x5c8b4cff957edae8
$r8 : 0x000000000000000c → 0x000000000000000c
$r9 : 0xffffed100d1883aa → 0x0000000000000000 → 0x0000000000000000
$r10 : 0xffff88806b90ac80 → 0xffff88806a634400 → 0x1e44a09cf7d0cbce → 0x1e44a09cf7d0cbce
$r11 : 0xffffed100d7215af → 0x0000000000000000 → 0x0000000000000000
$r12 : 0x0000000000000000 → 0x0000000000000000
$r13 : 0xdffffc0000000000 → 0xdffffc0000000000
$r14 : 0x0000000000000000 → 0x0000000000000000
$r15 : 0xffff888068c41cf0 → 0xffff888068c41b70 → 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 ────
0xffff88806c62fc20│+0x0000: 0xffff88806b90ac88 → 0xffff888069bd5700 → 0x0000000200000001 → 0x0000000200000001 ← $rsp
0xffff88806c62fc28│+0x0008: 0xffff888068c41d50 → 0xffff888067cde600 → 0xffffea0001b04e80 → 0x010000000000a016 → 0x010000000000a016
0xffff88806c62fc30│+0x0010: 0xffff88806b90ad18 → 0x0000000000000001 → 0x0000000000000001
0xffff88806c62fc38│+0x0018: 0xffff88806b90ad78 → 0xffff88806b90add8 → 0x0000000000000000 → 0x0000000000000000
0xffff88806c62fc40│+0x0020: 0xffff88806b90ae38 → 0x0000000000000000 → 0x0000000000000000
0xffff88806c62fc48│+0x0028: 0xffffea0001b04e80 → 0x010000000000a016 → 0x010000000000a016
0xffff88806c62fc50│+0x0030: 0x0000000000000000 → 0x0000000000000000
0xffff88806c62fc58│+0x0038: 0xffffffff00000000 → 0xffffffff00000000
──────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0xffffffff81bc11c2 <index_rbio_pages+1282> mov QWORD PTR [rsp+0x30], rax
0xffffffff81bc11c7 <index_rbio_pages+1287> mov QWORD PTR [rsp+0x28], rdx
0xffffffff81bc11cc <index_rbio_pages+1292> mov QWORD PTR [rsp+0x20], rcx
→ 0xffffffff81bc11d1 <index_rbio_pages+1297> call 0xffffffff815190b0 <__asan_report_store8_noabort>
↳ 0xffffffff815190b0 <__asan_report_store8_noabort+0> mov rcx, QWORD PTR [rsp]
0xffffffff815190b4 <__asan_report_store8_noabort+4> mov edx, 0x1
0xffffffff815190b9 <__asan_report_store8_noabort+9> mov esi, 0x8
0xffffffff815190be <__asan_report_store8_noabort+14> jmp 0xffffffff815185f0 <kasan_report>
0xffffffff815190c3 nop DWORD PTR [rax]
0xffffffff815190c6 nop WORD PTR cs:[rax+rax*1+0x0]
────────────────────────────────────────────────────────────────────────────────────────── arguments ────
__asan_report_store8_noabort (
long unsigned int var_0 = 0xffff88806b90ae38 → 0x0000000000000000 → 0x0000000000000000
)
──────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
[#1] Id 2, Name: "", stopped, reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────────── 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(worker=0xffff88806cfdf100, work=0xffff88806b90acc8)
[#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
gef➤
when local variable i = zero and page_index = 0xc, out-of-bound write occurs in rbio->bio_pages array.
Bug causes
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);
}local variable page_index becomes 0xc[1], rbio->bio_pages array occurs slab-out-of-bounds write in [2].
KASAN logs
[ 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]
[ 158.837964] CPU: 0 PID: 20 Comm: kworker/u4:1 Not tainted 5.0.21 #1
[ 158.837964] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 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] ? index_rbio_pages+0x516/0x800
[ 158.837964] kasan_report+0x13a/0x19b
[ 158.837964] ? index_rbio_pages+0x516/0x800
[ 158.837964] ? index_rbio_pages+0x516/0x800
[ 158.837964] index_rbio_pages+0x516/0x800
[ 158.837964] rmw_work+0xb9/0x650
[ 158.837964] ? __switch_to_asm+0x41/0x70
[ 158.837964] ? __switch_to_asm+0x35/0x70
[ 158.837964] ? __switch_to_asm+0x41/0x70
[ 158.837964] ? validate_rbio_for_rmw+0xd0/0xd0
[ 158.837964] ? __switch_to_asm+0x35/0x70
[ 158.837964] ? __switch_to_asm+0x41/0x70
[ 158.837964] ? __switch_to_asm+0x35/0x70
[ 158.837964] ? __switch_to_asm+0x41/0x70
[ 158.837964] ? __switch_to_asm+0x35/0x70
[ 158.837964] normal_work_helper+0x1cf/0xa50
[ 158.837964] ? read_word_at_a_time+0xe/0x20
[ 158.837964] ? strscpy+0x95/0x310
[ 158.837964] process_one_work+0x580/0x1210
[ 158.837964] worker_thread+0x8a/0xfc0
[ 158.837964] ? __kthread_parkme+0x73/0xf0
[ 158.837964] ? rescuer_thread+0xc60/0xc60
[ 158.837964] kthread+0x2a9/0x390
[ 158.837964] ? kthread_destroy_worker+0x90/0x90
[ 158.837964] ret_from_fork+0x35/0x40
[ 158.837964]
[ 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] flush_write_bio.isra.41+0x2c/0x70
[ 158.837964] btree_write_cache_pages+0x3bb/0x7f0
[ 158.837964] do_writepages+0x5c/0x130
[ 158.837964] __filemap_fdatawrite_range+0x175/0x230
[ 158.837964] btrfs_write_marked_extents+0x2b0/0x2f0
[ 158.837964] btrfs_sync_log+0x1006/0x24f0
[ 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]
[ 158.837964] Freed by task 1257:
[ 158.837964] __kasan_slab_free+0x132/0x180
[ 158.837964] kfree+0x99/0x1c0
[ 158.837964] kfree_skb+0x6f/0x180
[ 158.837964] unix_dgram_sendmsg+0x964/0x1010
[ 158.837964] sock_sendmsg+0xae/0xe0
[ 158.837964] ___sys_sendmsg+0x676/0x890
[ 158.837964] __sys_sendmsg+0xd9/0x170
[ 158.837964] do_syscall_64+0x8c/0x280
[ 158.837964] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 158.837964]
[ 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] The buggy address belongs to the page:
[ 158.837964] page:ffffea0001ae4280 count:1 mapcount:0 mapping:ffff88806cc01280 index:0x0 compound_mapcount: 0
[ 158.837964] flags: 0x100000000010200(slab|head)
[ 158.837964] raw: 0100000000010200 dead000000000100 dead000000000200 ffff88806cc01280
[ 158.837964] raw: 0000000000000000 00000000800c000c 00000001ffffffff 0000000000000000
[ 158.837964] page dumped because: kasan: bad access detected
[ 158.837964]
[ 158.837964] Memory state around the buggy address:
[ 158.837964] ffff88806b90ad00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 158.837964] ffff88806b90ad80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 158.837964] >ffff88806b90ae00: 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc fc
[ 158.837964] ^
[ 158.837964] ffff88806b90ae80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 158.837964] ffff88806b90af00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 158.837964] ==================================================================
[ 158.837964] Disabling lock debugging due to kernel taint
Conclusion
mounting crafted image can cause slab-out-of-bound write in index_rbio_pages function.
It can be used as malicious way.
Discoverer
Team bobfuzzer
Acknowledgments
This Project used ported version(to 5.0.21 and 5.3.14 linux kernel) of filesystem fuzzer 'JANUS' which developed by GeorgiaTech Systems Software & Security Lab(SSLab)
Thank you for the excellent fuzzer and paper below.