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