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-19814/
CVE/CVE-2019-19814/

Latest commit

 

Git stats

Files

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

CVE-2019-19814

Target

Linux kernel f2fs FileSystem

Linux Version Availablity
5.0.21 True

Bug Type

out of bounds write

Abstract

mounting crafted image can cause out-of-bounds-write vulnerability in __remove_dirty_segment

Reproduce

mkdir mnt
mount poc_2019_19814.img ./mnt

Details

Debug View

─────────────────────────────────────────────────────────── registers ────
$rax   : 0x000000000000003f  →  0x000000000000003f
$rbx   : 0xffff888068ad6c00  →  0xffffffff83d4a040  →  0xffffffff81c51d60 →  0x5441554156415741  →  0x5441554156415741
$rcx   : 0x1ffff1100d15adc0  →  0x1ffff1100d15adc0
$rdx   : 0xdffffc0000000000  →  0xdffffc0000000000
$rsp   : 0xffff88806546f6f0  →  0x0000000081ca3176  →  0x0000000081ca3176
$rbp   : 0xffff88806c00e600  →  0xffff88806c00dd80  →  0xffffffff84532580 →  0xffff88806cc21100  →  0xffff88806cc21980  →  0xffff88806cc24400  →  0xffff88806cc25500  →  0xffff88806cc26600
$rsi   : 0x0000000000000000  →  0x0000000000000000
$rdi   : 0xffff888068ad6e00  →  0xffff888068ad6f00  →  0x0000000000000000 →  0x0000000000000000
$rip   : 0xffffffff81c9eb54  →  0x448b48ff87a4d7e8  →  0x448b48ff87a4d7e8
$r8    : 0xffffed100ca8dedc  →  0x0000000000000000  →  0x0000000000000000
$r9    : 0xffffed100ca8dedc  →  0x0000000000000000  →  0x0000000000000000
$r10   : 0x0000000000000001  →  0x0000000000000001
$r11   : 0xffffed100ca8dedb  →  0x0000000000000000  →  0x0000000000000000
$r12   : 0x000000000000003f  →  0x000000000000003f
$r13   : 0x0000000000000000  →  0x0000000000000000
$r14   : 0xffff88806c00e680  →  0xffff888068ad6a00  →  0xffff888068ad6b00 →  0xffffffff83d4b1e0  →  0xffffffff81cad380  →  0x0000b94856415741  →  0x0000b94856415741
$r15   : 0x0000000000000000  →  0x0000000000000000
$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 ────
0xffff88806546f6f0│+0x0000: 0x0000000081ca3176  →  0x0000000081ca3176	 ← $rsp
0xffff88806546f6f8│+0x0008: 0x000000000000003f  →  0x000000000000003f
0xffff88806546f700│+0x0010: 0x0000000000000000  →  0x0000000000000000
0xffff88806546f708│+0x0018: 0xffff888068ad6c48  →  0xffff888064b98cc0  →0x0000000080000000  →  0x0000000080000000
0xffff88806546f710│+0x0020: 0xffff88806c00e600  →  0xffff88806c00dd80  →0xffffffff84532580  →  0xffff88806cc21100  →  0xffff88806cc21980  →  0xffff88806cc24400  →  0xffff88806cc25500
0xffff88806546f718│+0x0028: 0xffff88806beef180  →  0x000000000000ffff  →0x000000000000ffff
0xffff88806546f720│+0x0030: 0x00000000000003ff  →  0x00000000000003ff
0xffff88806546f728│+0x0038: 0xffff88806546f880  →  0xffff8880661ac4c0  →0x00000000000a41ed  →  0x00000000000a41ed
───────────────────────────────────────────────────────── code:x86:64 ────
   0xffffffff81c9eb46 <__remove_dirty_segment+1174> jmp    0xffffffff81c9e88a <__remove_dirty_segment+474>
   0xffffffff81c9eb4b <__remove_dirty_segment+1179> mov    QWORD PTR [rsp+0x8], rax
   0xffffffff81c9eb50 <__remove_dirty_segment+1184> mov    DWORD PTR [rsp+0x4], esi
 → 0xffffffff81c9eb54 <__remove_dirty_segment+1188> call   0xffffffff81519030 <__asan_report_load8_noabort>
   ↳  0xffffffff81519030 <__asan_report_load8_noabort+0> mov    rcx, QWORD PTR [rsp]
      0xffffffff81519034 <__asan_report_load8_noabort+4> xor    edx, edx
      0xffffffff81519036 <__asan_report_load8_noabort+6> mov    esi, 0x8
      0xffffffff8151903b <__asan_report_load8_noabort+11> jmp    0xffffffff815185f0 <kasan_report>
      0xffffffff81519040 <__asan_report_load16_noabort+0> mov    rcx, QWORD PTR [rsp]
      0xffffffff81519044 <__asan_report_load16_noabort+4> xor    edx, edx
─────────────────────────────────────────────────────────── arguments ────
__asan_report_load8_noabort (
   long unsigned int var_0 = 0xffff888068ad6e00 → 0xffff888068ad6f00 → 0x0000000000000000 → 0x0000000000000000
)
──────────────────────────────────────── source:fs/f2fs/segment.c+809 ────
    804
    805	 	if (dirty_type == DIRTY) {
    806	 		struct seg_entry *sentry = get_seg_entry(sbi, segno); // segno = 0
    807	 		enum dirty_type t = sentry->type;
    808
 →  809	 		if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) // KASAN here, t:63 (0x3f)
    810	 			dirty_i->nr_dirty[t]--; // can i use this?
    811
    812	 		if (get_valid_blocks(sbi, segno, true) == 0)
    813	 			clear_bit(GET_SEC_FROM_SEG(sbi, segno),
    814	 						dirty_i->victim_secmap);
───────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "", stopped, reason: BREAKPOINT
[#1] Id 2, Name: "", stopped, reason: BREAKPOINT
─────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff81c9eb54 → __remove_dirty_segment(sbi=0xffff88806c00e600, segno=0x0, dirty_type=<optimized out>)
[#1] 0xffffffff81c9ef8d → locate_dirty_segment(sbi=0xffff88806c00e600, segno=0x0)
[#2] 0xffffffff81cab008 → f2fs_invalidate_blocks(sbi=0xffff88806c00e600, addr=0x0)
[#3] 0xffffffff81c83de2 → truncate_node(dn=0xffff88806546f880)
[#4] 0xffffffff81c90d8c → f2fs_remove_inode_page(inode=0xffff8880661ac4c0)
[#5] 0xffffffff81bfd050 → f2fs_evict_inode(inode=0xffff8880661ac4c0)
[#6] 0xffffffff8158d030 → evict(inode=0xffff888068ad6e00)
[#7] 0xffffffff815802bd → __dentry_kill(dentry=0xffff88806b7e3300)
[#8] 0xffffffff8158068a → dentry_kill(dentry=0xffff88806b7e3300)
[#9] 0xffffffff81583e8c → dput(dentry=0xffff88806b7e3300)
──────────────────────────────────────────────────────────────────────────

Thread 1 hit Breakpoint 1, __remove_dirty_segment (sbi=0xffff88806c00e600, segno=0x0, dirty_type=<optimized out>) at fs/f2fs/segment.c:809
warning: Source file is more recent than executable.
809			if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) // KASAN here, t:63 (0x3f)
gef➤  p t
$1 = 63
gef➤

when local variable t(sentry->type) is 63, dirty_i->segmap[t] occurs out-of-bounds write

Bug Causes

fs/f2fs/segment.c:809 (link)

static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
		enum dirty_type dirty_type)
{
	struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);

	if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type]))
		dirty_i->nr_dirty[dirty_type]--;

	if (dirty_type == DIRTY) {
[1]		struct seg_entry *sentry = get_seg_entry(sbi, segno); // segno = 0
		enum dirty_type t = sentry->type;

[2]		if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t])) // KASAN here, t:63 (0x3f)
			dirty_i->nr_dirty[t]--;

__mutex_owner(lock) returns freed task_struct pointer

KASAN logs

[   59.585415] ==================================================================
[   59.585706] BUG: KASAN: slab-out-of-bounds in __remove_dirty_segment+0x4a9/0x550
[   59.585706] Read of size 8 at addr ffff888068ad6e00 by task mount/1861
[   59.585706]
[   59.585706] CPU: 0 PID: 1861 Comm: mount Tainted: G        W         5.0.21 #1
[   59.585706] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014
[   59.585706] Call Trace:
[   59.585706]  dump_stack+0x5b/0x8b
[   59.585706]  print_address_description+0x70/0x280
[   59.585706]  ? __remove_dirty_segment+0x4a9/0x550
[   59.585706]  kasan_report+0x13a/0x19b
[   59.585706]  ? __remove_dirty_segment+0x4a9/0x550
[   59.585706]  ? __remove_dirty_segment+0x4a9/0x550
[   59.585706]  __remove_dirty_segment+0x4a9/0x550
[   59.585706]  locate_dirty_segment+0x38d/0x450
[   59.585706]  f2fs_invalidate_blocks+0xf8/0x3b0
[   59.585706]  truncate_node+0x162/0xc10
[   59.585706]  ? truncate_dnode+0x1a0/0x1a0
[   59.585706]  ? f2fs_get_node_info+0xc60/0xc60
[   59.585706]  f2fs_remove_inode_page+0x25c/0x920
[   59.585706]  ? f2fs_get_dnode_of_data+0x16f0/0x16f0
[   59.585706]  ? f2fs_mark_inode_dirty_sync.part.30+0xd/0x30
[   59.585706]  ? f2fs_truncate+0x20d/0x350
[   59.585706]  f2fs_evict_inode+0x960/0xcd0
[   59.585706]  evict+0x290/0x570
[   59.585706]  ? dentry_unlink_inode+0x2fb/0x3b0
[   59.585706]  __dentry_kill+0x2bd/0x600
[   59.585706]  dentry_kill+0x8a/0x4e0
[   59.585706]  dput+0x29c/0x3c0
[   59.585706]  f2fs_fill_super+0x458d/0x6440
[   59.585706]  ? f2fs_commit_super+0x640/0x640
[   59.585706]  ? f2fs_commit_super+0x640/0x640
[   59.585706]  ? mount_bdev+0x25d/0x310
[   59.585706]  mount_bdev+0x25d/0x310
[   59.585706]  mount_fs+0xd0/0x320
[   59.585706]  ? emergency_thaw_all+0x180/0x180
[   59.585706]  ? memcpy+0x34/0x50
[   59.585706]  vfs_kern_mount+0x5f/0x310
[   59.585706]  do_mount+0x35b/0x2720
[   59.585706]  ? copy_mount_string+0x20/0x20
[   59.585706]  ? lockref_put_return+0x130/0x130
[   59.585706]  ? __kasan_slab_free+0x147/0x180
[   59.585706]  ? kasan_unpoison_shadow+0x31/0x40
[   59.585706]  ? __kasan_kmalloc+0xd5/0xf0
[   59.585706]  ? strndup_user+0x42/0x90
[   59.585706]  ? __kmalloc_track_caller+0xfd/0x1d0
[   59.585706]  ? _copy_from_user+0x73/0xa0
[   59.585706]  ? memdup_user+0x39/0x60
[   59.585706]  ksys_mount+0x79/0xc0
[   59.585706]  __x64_sys_mount+0xb5/0x150
[   59.585706]  do_syscall_64+0x8c/0x280
[   59.585706]  ? prepare_exit_to_usermode+0xe1/0x140
[   59.585706]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[   59.585706] RIP: 0033:0x7fbecd98c48a
[   59.585706] Code: 48 8b 0d 11 fa 2a 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d de f9 2a 00 f7 d8 64 89 01 48
[   59.585706] RSP: 002b:00007ffe9e5f3598 EFLAGS: 00000202 ORIG_RAX: 00000000000000a5
[   59.585706] RAX: ffffffffffffffda RBX: 000055eb9eec0080 RCX: 00007fbecd98c48a
[   59.585706] RDX: 000055eb9eec2760 RSI: 000055eb9eec1f60 RDI: 000055eb9eec6600
[   59.585706] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000020
[   59.585706] R10: 00000000c0ed0000 R11: 0000000000000202 R12: 000055eb9eec6600
[   59.585706] R13: 000055eb9eec2760 R14: 0000000000000000 R15: 00000000ffffffff
[   59.585706]
[   59.585706] Allocated by task 0:
[   59.585706] (stack is not available)
[   59.585706]
[   59.585706] Freed by task 0:
[   59.585706] (stack is not available)
[   59.585706]
[   59.585706] The buggy address belongs to the object at ffff888068ad6e00
[   59.585706]  which belongs to the cache kmalloc-192 of size 192
[   59.585706] The buggy address is located 0 bytes inside of
[   59.585706]  192-byte region [ffff888068ad6e00, ffff888068ad6ec0)
[   59.585706] The buggy address belongs to the page:
[   59.585706] page:ffffea0001a2b580 count:1 mapcount:0 mapping:ffff88806cc01500 index:0x0
[   59.585706] flags: 0x100000000000200(slab)
[   59.585706] raw: 0100000000000200 dead000000000100 dead000000000200 ffff88806cc01500
[   59.585706] raw: 0000000000000000 0000000080100010 00000001ffffffff 0000000000000000
[   59.585706] page dumped because: kasan: bad access detected
[   59.585706]
[   59.585706] Memory state around the buggy address:
[   59.585706]  ffff888068ad6d00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   59.585706]  ffff888068ad6d80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   59.585706] >ffff888068ad6e00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   59.585706]                    ^
[   59.585706]  ffff888068ad6e80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   59.585706]  ffff888068ad6f00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[   59.585706] ==================================================================

Conclusion

Mounting Crafted image can cause slab-out-of-bounds write in __remove_dirty_segment function.

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.