Headline
CVE-2019-19448: CVE/CVE-2019-19448 at master · bobfuzzer/CVE
In the Linux kernel 5.0.21 and 5.3.11, mounting a crafted btrfs filesystem image, performing some operations, and then making a syncfs system call can lead to a use-after-free in try_merge_free_space in fs/btrfs/free-space-cache.c because the pointer to a left data structure can be the same as the pointer to a right data structure.
- CVE-2019-19448
- Target
- Bug Type
- Abstract
- Reproduce
- Details
- Debug View
- Bug Causes
- KASAN logs
- Conclusion
- Discoverer
- Acknowledgments
Target
Linux kernel btrfs FileSystem
Linux Version
Availablity
5.0.21
True
5.3.11
True
Bug Type
Use After Free
Abstract
syncfs syscall after some operation with crafted image can cause use-after-free vulnerability in try_merge_free_space
Reproduce
gcc -o poc poc_2019_19448.c mkdir mnt mount poc_2019_19448.img ./mnt cp poc ./mnt/ cd mnt ./poc
Details****Debug View
─────────────────────────────────────────────────────────── registers ────
$rax : 0x0000000000000001 → 0x0000000000000001
$rbx : 0xffff88806cf59138 → 0x0000000000000000 → 0x0000000000000000
$rcx : 0xffff888067a6aa80 → 0xffff88806cf59138 → 0x0000000000000000 → 0x0000000000000000
$rdx : 0x00000000000a17c8 → 0x00000000000a17c8
$rsp : 0xffff8880649bfa60 → 0x0000000000001001 → 0x0000000000001001
$rbp : 0xffff88806a2491a0 → 0xffff88806a2491a0 → [loop detected]
$rsi : 0xffff88806a6388c0 → 0x0000607f92802900 → 0x0000607f92802900
$rdi : 0xffff88806cf59168 → 0x0000000000000000 → 0x0000000000000000
$rip : 0xffffffff81625d82 → 0x7d8349ffcbea59e8 → 0x7d8349ffcbea59e8
$r8 : 0x00000000214fbdaa → 0x00000000214fbdaa
$r9 : 0xffffed100daa632c → 0x0000000000000000 → 0x0000000000000000
$r10 : 0x0000000000000001 → 0x0000000000000001
$r11 : 0xffffed100daa632b → 0x0000000000000000 → 0x0000000000000000
$r12 : 0xffff888067a7c600 → 0x0000000000000001 → 0x0000000000000001
$r13 : 0xffff88806cf59138 → 0x0000000000000000 → 0x0000000000000000
$r14 : 0xffff888067a7c608 → 0xffff88806a2490d0 → 0x0000000000000001 → 0x0000000000000001
$r15 : 0x0000000001c04000 → 0x0000000001c04000
$eflags: [zero carry parity adjust SIGN trap INTERRUPT direction overflowresume virtualx86 identification]
$cs: 0x0010 $ss: 0x0018 $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
─────────────────────────────────────────────────────────────── stack ────
0xffff8880649bfa60│+0x0000: 0x0000000000001001 → 0x0000000000001001 ← $rsp
0xffff8880649bfa68│+0x0008: 0xffff88806a2491c0 → 0x0000000000007000 →0x0000000000007000
0xffff8880649bfa70│+0x0010: 0x000000016a2491a0 → 0x000000016a2491a0
0xffff8880649bfa78│+0x0018: 0xffff88806a2491b8 → 0x0000000001c04000 →0x0000000001c04000
0xffff8880649bfa80│+0x0020: 0xffff88806cf59158 → 0x0000000000006000 →0x0000000000006000
0xffff8880649bfa88│+0x0028: 0xffffffff81625cd2 → 0x5541564118468d48 →0x5541564118468d48
0xffff8880649bfa90│+0x0030: 0xffff888067a7c600 → 0x0000000000000001 →0x0000000000000001
0xffff8880649bfa98│+0x0038: 0xffff88806a2491a0 → 0xffff88806a2491a0 →[loop detected]
───────────────────────────────────────────────────────── code:x86:64 ────
0xffffffff81625d79 <try_merge_free_space+169> je 0xffffffff81625d92 <try_merge_free_space+194>
0xffffffff81625d7b <try_merge_free_space+171> lea rdi, [r13+0x30]
0xffffffff81625d7f <try_merge_free_space+175> mov BYTE PTR [rsp], al
→ 0xffffffff81625d82 <try_merge_free_space+178> call 0xffffffff812e47e0 <__asan_load8>
↳ 0xffffffff812e47e0 <__asan_load8_noabort+0> movabs rax, 0xffff7fffffffffff
0xffffffff812e47ea <__asan_load8_noabort+10> mov rcx, QWORD PTR [rsp]
0xffffffff812e47ee <__asan_load8_noabort+14> cmp rdi, rax
0xffffffff812e47f1 <__asan_load8_noabort+17> jbe 0xffffffff812e4824 <__asan_load8+68>
0xffffffff812e47f3 <__asan_load8_noabort+19> lea rax, [rdi+0x7]
0xffffffff812e47f7 <__asan_load8_noabort+23> mov rdx, rax
─────────────────────────────────────────────────────────── arguments ────
__asan_load8 (
long unsigned int var_0 = 0xffff88806cf59168 → 0x0000000000000000 → 0x0000000000000000
)
───────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
[#1] Id 2, Name: "", stopped, reason: BREAKPOINT
─────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff81625d82 → try_merge_free_space(ctl=0xffff888067a7c600, info=0xffff88806a2491a0, update_stat=<optimized out>)
[#1] 0xffffffff81629539 → __btrfs_add_free_space(fs_info=<optimized out>,ctl=0xffff888067a7c600, offset=<optimized out>, bytes=0x1000)
[#2] 0xffffffff81580277 → btrfs_add_free_space(block_group=<optimized out>, block_group=<optimized out>, size=<optimized out>, bytenr=<optimized out>)
[#3] 0xffffffff81580277 → unpin_extent_range(fs_info=0xffff8880650968a0, start=0x1c04000, end=<optimized out>, return_free_space=<optimized out>)
[#4] 0xffffffff815849ba → btrfs_finish_extent_commit(trans=0xffff88806bb3a150)
[#5] 0xffffffff815ae355 → btrfs_commit_transaction(trans=0xffff88806bb3a150)
[#6] 0xffffffff813499e2 → __sync_filesystem(wait=<optimized out>, sb=<optimized out>)
[#7] 0xffffffff813499e2 → sync_filesystem(sb=0xffff88806487f700)
[#8] 0xffffffff81349b07 → __do_sys_syncfs(fd=<optimized out>)
[#9] 0xffffffff81349b07 → __se_sys_syncfs(fd=<optimized out>)
──────────────────────────────────────────────────────────────────────────
Thread 2 hit Breakpoint 1, 0xffffffff81625d82 in try_merge_free_space (ctl=0xffff888067a7c600, info=0xffff88806a2491a0, update_stat=<optimized out>) at fs/btrfs/free-space-cache.c:2191
2191 in fs/btrfs/free-space-cache.c
gef➤ p right_info
$13 = (struct btrfs_free_space *) 0xffff88806cf59138
gef➤ p left_info
$14 = (struct btrfs_free_space *) 0xffff88806cf59138
gef➤
local variable right_info equals left_info.
Bug Causes
fs/btrfs/free-space-cache.c:2191 (link)
static bool try_merge_free_space(struct btrfs_free_space_ctl *ctl, struct btrfs_free_space *info, bool update_stat) { struct btrfs_free_space *left_info; struct btrfs_free_space *right_info; bool merged = false; u64 offset = info->offset; u64 bytes = info->bytes;
/\*
\* first we want to see if there is free space adjacent to the range we
\* are adding, if there is remove that struct and add a new one to
\* cover the entire range
\*/
[1] right_info = tree_search_offset(ctl, offset + bytes, 0, 0); if (right_info && rb_prev(&right_info->offset_index)) left_info = rb_entry(rb_prev(&right_info->offset_index), struct btrfs_free_space, offset_index); else [1] left_info = tree_search_offset(ctl, offset - 1, 0, 0);
if (right\_info && !right\_info->bitmap) {
if (update\_stat)
unlink\_free\_space(ctl, right\_info);
else
\_\_unlink\_free\_space(ctl, right\_info);
info->bytes += right\_info->bytes;
[2] kmem_cache_free(btrfs_free_space_cachep, right_info); merged = true; }
[3] if (left_info && !left_info->bitmap && left_info->offset + left_info->bytes == offset) { if (update_stat) unlink_free_space(ctl, left_info); else __unlink_free_space(ctl, left_info); info->offset = left_info->offset; info->bytes += left_info->bytes; kmem_cache_free(btrfs_free_space_cachep, left_info); merged = true; }
return merged;
}
in [1] tree_search_offset returns same ptr(right_info == left_info), and use left_info[3] after freeing right_info[2]
KASAN logs
[ 129.924437] ==================================================================
[ 129.924437] BUG: KASAN: use-after-free in try_merge_free_space+0xb7/0x2e0
[ 129.924437] Read of size 8 at addr ffff88806cf59168 by task poc/220
[ 129.924437]
[ 129.924437] CPU: 1 PID: 220 Comm: poc Not tainted 5.3.11 #1
[ 129.924437] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 129.924437] Call Trace:
[ 129.924437] dump_stack+0x76/0xab
[ 129.924437] print_address_description+0x70/0x3a0
[ 129.924437] ? try_merge_free_space+0xb7/0x2e0
[ 129.924437] __kasan_report+0x13a/0x19b
[ 129.924437] ? try_merge_free_space+0xb7/0x2e0
[ 129.924437] ? try_merge_free_space+0xb7/0x2e0
[ 129.924437] kasan_report+0xe/0x20
[ 129.924437] try_merge_free_space+0xb7/0x2e0
[ 129.924437] ? try_merge_free_space+0x2/0x2e0
[ 129.924437] __btrfs_add_free_space+0x89/0x5f0
[ 129.924437] ? rb_erase+0x1bd/0x830
[ 129.924437] ? block_group_cache_tree_search+0x12f/0x150
[ 129.924437] unpin_extent_range+0x537/0x930
[ 129.924437] ? __set_extent_bit+0x850/0x850
[ 129.924437] ? __exclude_logged_extent+0x150/0x150
[ 129.924437] ? cache_state_if_flags.part.32+0x10/0x30
[ 129.924437] btrfs_finish_extent_commit+0x13a/0x450
[ 129.924437] ? pagecache_get_page+0x20b/0x330
[ 129.924437] ? __wait_on_bit+0x150/0x150
[ 129.924437] ? btrfs_prepare_extent_commit+0x190/0x190
[ 129.924437] ? __find_get_block+0x40d/0x450
[ 129.924437] ? write_all_supers+0x635/0x1590
[ 129.924437] btrfs_commit_transaction+0xd15/0x1100
[ 129.924437] ? btrfs_apply_pending_changes+0x80/0x80
[ 129.924437] ? btrfs_attach_transaction_barrier+0x19/0x50
[ 129.924437] sync_filesystem+0xd2/0x110
[ 129.924437] __x64_sys_syncfs+0x57/0x90
[ 129.924437] do_syscall_64+0x5e/0x190
[ 129.924437] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 129.924437] RIP: 0033:0x7f6306075469
[ 129.924437] 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
[ 129.924437] RSP: 002b:00007ffd2a5aeba8 EFLAGS: 00000286 ORIG_RAX: 0000000000000132
[ 129.924437] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f6306075469
[ 129.924437] RDX: ffffffffffffff98 RSI: 00007f6306075469 RDI: 0000000000000003
[ 129.924437] RBP: 00007ffd2a5b2d40 R08: 00007ffd2a5b2e28 R09: 00007ffd2a5b2e28
[ 129.924437] R10: 00007ffd2a5b2e28 R11: 0000000000000286 R12: 00005606df5a65d0
[ 129.924437] R13: 00007ffd2a5b2e20 R14: 0000000000000000 R15: 0000000000000000
[ 129.924437]
[ 129.924437] Allocated by task 220:
[ 129.924437] save_stack+0x19/0x80
[ 129.924437] __kasan_kmalloc+0xd5/0xf0
[ 129.924437] kmem_cache_alloc+0xb3/0x1d0
[ 129.924437] __btrfs_add_free_space+0x39/0x5f0
[ 129.924437] unpin_extent_range+0x537/0x930
[ 129.924437] btrfs_finish_extent_commit+0x13a/0x450
[ 129.924437] btrfs_commit_transaction+0xd15/0x1100
[ 129.924437] sync_filesystem+0xd2/0x110
[ 129.924437] __x64_sys_syncfs+0x57/0x90
[ 129.924437] do_syscall_64+0x5e/0x190
[ 129.924437] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 129.924437]
[ 129.924437] Freed by task 220:
[ 129.924437] save_stack+0x19/0x80
[ 129.924437] __kasan_slab_free+0x132/0x180
[ 129.924437] kmem_cache_free+0x75/0x280
[ 129.924437] try_merge_free_space+0x276/0x2e0
[ 129.924437] __btrfs_add_free_space+0x89/0x5f0
[ 129.924437] unpin_extent_range+0x537/0x930
[ 129.924437] btrfs_finish_extent_commit+0x13a/0x450
[ 129.924437] btrfs_commit_transaction+0xd15/0x1100
[ 129.924437] sync_filesystem+0xd2/0x110
[ 129.924437] __x64_sys_syncfs+0x57/0x90
[ 129.924437] do_syscall_64+0x5e/0x190
[ 129.924437] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 129.924437]
[ 129.924437] The buggy address belongs to the object at ffff88806cf59138
[ 129.924437] which belongs to the cache btrfs_free_space of size 72
[ 129.924437] The buggy address is located 48 bytes inside of
[ 129.924437] 72-byte region [ffff88806cf59138, ffff88806cf59180)
[ 129.924437] The buggy address belongs to the page:
[ 129.924437] page:ffffea0001b3d640 refcount:1 mapcount:0 mapping:ffff88806a6388c0 index:0x0
[ 129.924437] flags: 0x100000000000200(slab)
[ 129.924437] raw: 0100000000000200 dead000000000100 dead000000000122 ffff88806a6388c0
[ 129.924437] raw: 0000000000000000 0000000080270027 00000001ffffffff 0000000000000000
[ 129.924437] page dumped because: kasan: bad access detected
[ 129.924437]
[ 129.924437] Memory state around the buggy address:
[ 129.924437] ffff88806cf59000: 00 00 00 00 00 00 00 00 00 fc fc fc fc 00 00 00
[ 129.924437] ffff88806cf59080: 00 00 00 00 00 00 fc fc fc fc 00 00 00 00 00 00
[ 129.924437] >ffff88806cf59100: 00 00 00 fc fc fc fc fb fb fb fb fb fb fb fb fb
[ 129.924437] ^
[ 129.924437] ffff88806cf59180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 129.924437] ffff88806cf59200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 129.924437] ==================================================================
[ 129.924437] Disabling lock debugging due to kernel taint
[ 130.400869] WARNING: CPU: 1 PID: 220 at fs/btrfs/free-space-cache.c:1480 tree_insert_offset+0x75/0xe0
[ 130.400869] Modules linked in:
[ 130.400869] CPU: 1 PID: 220 Comm: poc Tainted: G B 5.3.11 #1
[ 130.400869] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[ 130.400869] RIP: 0010:tree_insert_offset+0x75/0xe0
[ 130.400869] Code: ff 49 8b 1f 48 85 db 74 2a 48 8d 7b 18 e8 e3 ee cb ff 4c 39 63 18 76 bf 4c 8d 7b 10 eb d8 e8 d2 ee cb ff 48 83 7b 30 00 75 ee <0f> 0b b8 ef ff ff ff eb 3e 4c 89 f7 e8 3a ef cb ff 49 8d 7e 08 49
[ 130.400869] RSP: 0018:ffff8880649bfa60 EFLAGS: 00000246
[ 130.400869] RAX: 0000000000000000 RBX: ffff88806a249068 RCX: ffffffff8162590e
[ 130.400869] RDX: dffffc0000000000 RSI: 0000000001c09000 RDI: ffff88806a249098
[ 130.400869] RBP: ffff88806a2490d0 R08: 0000000000000000 R09: ffffed100c937f4e
[ 130.400869] R10: 0000000000000001 R11: ffffed100c937f4e R12: 0000000001c09000
[ 130.400869] R13: 0000000000000000 R14: ffff88806a249270 R15: ffff88806a2490e0
[ 130.400869] FS: 00007f630654e440(0000) GS:ffff88806d500000(0000) knlGS:0000000000000000
[ 130.400869] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 130.400869] CR2: 00007f07b731d010 CR3: 0000000067834000 CR4: 00000000000006e0
[ 130.400869] Call Trace:
[ 130.400869] link_free_space+0x3f/0x80
[ 130.400869] __btrfs_add_free_space+0xb8/0x5f0
[ 130.400869] ? rb_erase+0x1bd/0x830
[ 130.400869] unpin_extent_range+0x537/0x930
[ 130.400869] ? __set_extent_bit+0x850/0x850
[ 130.400869] ? __exclude_logged_extent+0x150/0x150
[ 130.400869] ? cache_state_if_flags.part.32+0x10/0x30
[ 130.400869] btrfs_finish_extent_commit+0x13a/0x450
[ 130.400869] ? pagecache_get_page+0x20b/0x330
[ 130.400869] ? __wait_on_bit+0x150/0x150
[ 130.400869] ? btrfs_prepare_extent_commit+0x190/0x190
[ 130.400869] ? __find_get_block+0x40d/0x450
[ 130.400869] ? write_all_supers+0x635/0x1590
[ 130.400869] btrfs_commit_transaction+0xd15/0x1100
[ 130.400869] ? btrfs_apply_pending_changes+0x80/0x80
[ 130.400869] ? btrfs_attach_transaction_barrier+0x19/0x50
[ 130.400869] sync_filesystem+0xd2/0x110
[ 130.400869] __x64_sys_syncfs+0x57/0x90
[ 130.400869] do_syscall_64+0x5e/0x190
[ 130.400869] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 130.400869] RIP: 0033:0x7f6306075469
[ 130.400869] 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
[ 130.400869] RSP: 002b:00007ffd2a5aeba8 EFLAGS: 00000286 ORIG_RAX: 0000000000000132
[ 130.400869] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f6306075469
[ 130.400869] RDX: ffffffffffffff98 RSI: 00007f6306075469 RDI: 0000000000000003
[ 130.400869] RBP: 00007ffd2a5b2d40 R08: 00007ffd2a5b2e28 R09: 00007ffd2a5b2e28
[ 130.400869] R10: 00007ffd2a5b2e28 R11: 0000000000000286 R12: 00005606df5a65d0
[ 130.400869] R13: 00007ffd2a5b2e20 R14: 0000000000000000 R15: 0000000000000000
[ 130.400869] ---[ end trace af3d70351e521739 ]---
[ 130.451152] BTRFS critical (device loop0): unable to add free space :-17
[ 130.512290] BTRFS critical (device loop0): unable to add free space :-17
Conclusion
in try_merge_free_space function, local variable right_info and left_info can be same value.
it can be lead to arbitrary-address-free or double-free vulnerability by attacker.
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.
- sslab-gatech/janus
- Fuzzing File Systems via Two-Dimensional Input Space Exploration (IEEE S&P 2019)