CVE-2019-19449
Target
Linux Kernel 5.0.21 f2fs FileSystem
Bug Type
slab-out-of-bounds read
Abstract
mounting crafted image can cause slab-out-of-bounds read in init_min_max_mtime
it can be not only local(mount f2fs image in local shell), but also remote(mount corrupted(with crafted f2fs image) USB or other storage)
Reproduce
mkdir mnt
mount poc_2019_19449.img ./mntDetails
Debug view
───────────────────────────────────────────────────────────────────────────── registers ────
$rax : 0x001e00007f200000 → 0x001e00007f200000
$rbx : 0xffff8880685e4e00 → (bad)
$rcx : 0xffff88806c7870c0 → 0x0000000000000000 → 0x0000000000000000
$rdx : 0xdffffc0000000000 → 0xdffffc0000000000
$rsp : 0xffff8880659df918 → 0x1ffff1100cb3bf38 → 0x1ffff1100cb3bf38
$rbp : 0x0000000000000019 → 0x0000000000000019
$rsi : 0xffff88806c786d00 → 0x00000000000300c3 → 0x00000000000300c3
$rdi : 0xffff88806c7870e0 → 0x0000000000000000 → 0x0000000000000000
$rip : 0xffffffff81798228 → 0x4c8b48ffb22af3e8 → 0x4c8b48ffb22af3e8
$r8 : 0xffffffff812b9375 → <__kasan_kmalloc+213> mov DWORD PTR [r12+0x4], eax
$r9 : 0xffffffff81798142 → 0x4800000088bb8d48 → 0x4800000088bb8d48
$r10 : 0xffffffff81798142 → 0x4800000088bb8d48 → 0x4800000088bb8d48
$r11 : 0xffffffff81757e7a → 0x01e1840fc389c085 → 0x01e1840fc389c085
$r12 : 0x0000000000000401 → 0x0000000000000401
$r13 : 0x0000000000000000 → 0x0000000000000000
$r14 : 0xffff8880668d3380 → add BYTE PTR [rdi+0x5e], cl
$r15 : 0xffff8880668d3300 → cmp BYTE PTR [rbx], 0x8d
$eflags: [carry parity adjust zero sign trap INTERRUPT direction overflow resume virtualx86 identification]
$cs: 0x0010 $ss: 0x0018 $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
───────────────────────────────────────────────────────────────────────────────── stack ────
[!] Unmapped address
─────────────────────────────────────────────────────────────────────────── code:x86:64 ────
0xffffffff8179821b <f2fs_build_segment_manager+12043> lea rcx, [rsi+rcx*8]
0xffffffff8179821f <f2fs_build_segment_manager+12047> lea rdi, [rcx+0x20]
0xffffffff81798223 <f2fs_build_segment_manager+12051> mov QWORD PTR [rsp+0x18], rcx
0xffffffff81798228 <f2fs_build_segment_manager+12056> call 0xffffffff812bad20 <__asan_load8>
0xffffffff8179822d <f2fs_build_segment_manager+12061> mov rcx, QWORD PTR [rsp+0x18]
0xffffffff81798232 <f2fs_build_segment_manager+12066> mov rax, QWORD PTR [rsp+0x30]
0xffffffff81798237 <f2fs_build_segment_manager+12071> mov rsi, QWORD PTR [rsp+0x28]
0xffffffff8179823c <f2fs_build_segment_manager+12076> add rax, QWORD PTR [rcx+0x20]
0xffffffff81798240 <f2fs_build_segment_manager+12080> cmp ebp, DWORD PTR [rsp+0x8]
───────────────────────────────────────────────────────────────────────────── arguments ────
__asan_load8 (
long unsigned int var_0 = 0xffff88806c7870e0 → 0x0000000000000000 → 0x0000000000000000
)
───────────────────────────────────────────────────────── source:fs/f2fs/segment.c+4230 ────
4225 for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
4226 unsigned int i;
4227 unsigned long long mtime = 0;
4228
4229 for (i = 0; i < sbi->segs_per_sec; i++)
→ 4230 mtime += get_seg_entry(sbi, segno + i)->mtime;
4231
4232 mtime = div_u64(mtime, sbi->segs_per_sec);
4233
4234 if (sit_i->min_mtime > mtime)
4235 sit_i->min_mtime = mtime;
─────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, stopped 0xffffffff81798228 in init_min_max_mtime (), reason: SINGLE STEP
[#1] Id 2, stopped 0xffffffff812df57e in may_lookup (), reason: SINGLE STEP
───────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0xffffffff81798228 → init_min_max_mtime(sbi=<optimized out>)
[#1] 0xffffffff81798228 → f2fs_build_segment_manager(sbi=0xffff8880668d3300)
[#2] 0xffffffff81757e7a → f2fs_fill_super(sb=<optimized out>, data=<optimized out>, silent=<optimized out>)
[#3] 0xffffffff812cef1b → mount_bdev(fs_type=<optimized out>, flags=0x0, dev_name=<optimized out>, data=<optimized out>, fill_super=0xffffffff81756200 <f2fs_fill_super>)
[#4] 0xffffffff812cfd1c → mount_fs(type=0xffffffff83224f40 <f2fs_fs_type>, flags=0x0, name=0xffff8880662ff100 "/dev/loop0", data=0x0 <irq_stack_union>)
[#5] 0xffffffff812fd7ff → vfs_kern_mount(type=0xffffffff83224f40 <f2fs_fs_type>, flags=0x0, name=0xffff8880662ff100 "/dev/loop0", data=0x0 <irq_stack_union>)
[#6] 0xffffffff8130218a → do_new_mount(data=<optimized out>, name=<optimized out>, mnt_flags=<optimized out>, sb_flags=<optimized out>, fstype=<optimized out>, path=<optimized out>)
[#7] 0xffffffff8130218a → do_mount(dev_name=0xffff8880662ff100 "/dev/loop0", dir_name=<optimized out>, type_page=<optimized out>, flags=0x0, data_page=0x0 <irq_stack_union>)
[#8] 0xffffffff81303879 → ksys_mount(dev_name=<optimized out>, dir_name=0x55e6e0209f60 "/root/images/mnt", type=<optimized out>, flags=0xc0ed0000, data=<optimized out>)
[#9] 0xffffffff8130391d → __do_sys_mount(data=<optimized out>, flags=<optimized out>, type=<optimized out>, dir_name=<optimized out>, dev_name=<optimized out>)
────────────────────────────────────────────────────────────────────────────────────────────
Bug causes
static void init_min_max_mtime(struct f2fs_sb_info *sbi)
{
struct sit_info *sit_i = SIT_I(sbi);
unsigned int segno;
down_write(&sit_i->sentry_lock);
sit_i->min_mtime = ULLONG_MAX;
for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
unsigned int i;
unsigned long long mtime = 0;
for (i = 0; i < sbi->segs_per_sec; i++)
(1) mtime += get_seg_entry(sbi, segno + i)->mtime;
mtime = div_u64(mtime, sbi->segs_per_sec);
if (sit_i->min_mtime > mtime)
sit_i->min_mtime = mtime;
}
sit_i->max_mtime = get_mtime(sbi, false);
up_write(&sit_i->sentry_lock);
}
static inline struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi,
unsigned int segno)
{
struct sit_info *sit_i = SIT_I(sbi);
return &sit_i->sentries[segno];
}
when segno + i greater than 0x19, get_seg_entry returns out-of-bound memory pointer.
KASAN logs
[ 208.086266] F2FS-fs (loop0): Fix alignment : done, start(4096) end(436224000) block(12288)
[ 208.178227] F2FS-fs (loop0): Fix alignment : done, start(4096) end(16384) block(10752)
[ 208.261239] F2FS-fs (loop0): invalid cp_pack_total_block_count:33023
[ 208.579490] ==================================================================
[ 208.579490] BUG: KASAN: slab-out-of-bounds in f2fs_build_segment_manager+0x2f1d/0x2f80
[ 208.579490] Read of size 8 at addr ffff88806c7870e0 by task mount/1833
[ 208.579490]
[ 208.579490] CPU: 0 PID: 1833 Comm: mount Not tainted 5.0.21 #2
[ 208.579490] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 208.579490] Call Trace:
[ 208.579490] dump_stack+0x5b/0x8b
[ 208.579490] print_address_description+0x70/0x280
[ 208.579490] ? f2fs_build_segment_manager+0x2f1d/0x2f80
[ 208.579490] kasan_report+0x13a/0x19b
[ 208.579490] ? f2fs_build_segment_manager+0x2f1d/0x2f80
[ 208.579490] ? __asan_store4+0x60/0x80
[ 208.579490] ? f2fs_build_segment_manager+0x2f1d/0x2f80
[ 208.579490] f2fs_build_segment_manager+0x2f1d/0x2f80
[ 208.579490] ? f2fs_flush_sit_entries+0x1190/0x1190
[ 208.579490] ? cpumask_next+0x16/0x20
[ 208.579490] f2fs_fill_super+0x1c7a/0x2d50
[ 208.579490] ? f2fs_commit_super+0x2f0/0x2f0
[ 208.579490] ? sb_set_blocksize+0x16/0x70
[ 208.579490] ? set_blocksize+0x83/0x130
[ 208.579490] ? f2fs_commit_super+0x2f0/0x2f0
[ 208.579490] mount_bdev+0x1bb/0x200
[ 208.579490] mount_fs+0xac/0x210
[ 208.579490] ? emergency_thaw_all+0xa0/0xa0
[ 208.579490] ? memcpy+0x34/0x50
[ 208.579490] ? __init_waitqueue_head+0x29/0x30
[ 208.579490] vfs_kern_mount+0x5f/0x190
[ 208.579490] do_mount+0x30a/0x1500
[ 208.579490] ? __schedule+0x5d9/0xb40
[ 208.579490] ? copy_mount_string+0x20/0x20
[ 208.579490] ? __sched_text_start+0x8/0x8
[ 208.579490] ? kasan_unpoison_shadow+0x31/0x40
[ 208.579490] ? __kasan_kmalloc+0xd5/0xf0
[ 208.579490] ? strndup_user+0x3a/0x60
[ 208.579490] ? __kmalloc_track_caller+0xc7/0x1c0
[ 208.579490] ? _copy_from_user+0x61/0x90
[ 208.579490] ? memdup_user+0x39/0x60
[ 208.579490] ksys_mount+0x79/0xc0
[ 208.579490] __x64_sys_mount+0x5d/0x70
[ 208.579490] do_syscall_64+0x5e/0x150
[ 208.579490] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 208.579490] RIP: 0033:0x7fd6debe348a
[ 208.579490] 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
[ 208.579490] RSP: 002b:00007ffd5e0515b8 EFLAGS: 00000206 ORIG_RAX: 00000000000000a5
[ 208.579490] RAX: ffffffffffffffda RBX: 000055e6e0208080 RCX: 00007fd6debe348a
[ 208.579490] RDX: 000055e6e020a760 RSI: 000055e6e0209f60 RDI: 000055e6e020de90
[ 208.579490] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000020
[ 208.579490] R10: 00000000c0ed0000 R11: 0000000000000206 R12: 000055e6e020de90
[ 208.579490] R13: 000055e6e020a760 R14: 0000000000000000 R15: 00000000ffffffff
[ 208.579490]
[ 208.579490] Allocated by task 1833:
[ 208.579490] __kasan_kmalloc+0xd5/0xf0
[ 208.579490] f2fs_build_segment_manager+0x472/0x2f80
[ 208.579490] f2fs_fill_super+0x1c7a/0x2d50
[ 208.579490] mount_bdev+0x1bb/0x200
[ 208.579490] mount_fs+0xac/0x210
[ 208.579490] vfs_kern_mount+0x5f/0x190
[ 208.579490] do_mount+0x30a/0x1500
[ 208.579490] ksys_mount+0x79/0xc0
[ 208.579490] __x64_sys_mount+0x5d/0x70
[ 208.579490] do_syscall_64+0x5e/0x150
[ 208.579490] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 208.579490]
[ 208.579490] Freed by task 1243:
[ 208.579490] __kasan_slab_free+0x132/0x180
[ 208.579490] kfree+0x8c/0x1a0
[ 208.579490] consume_skb+0x40/0xd0
[ 208.579490] skb_free_datagram+0xd/0x60
[ 208.579490] unix_dgram_recvmsg+0x5cb/0x810
[ 208.579490] ___sys_recvmsg+0x1bc/0x390
[ 208.579490] __sys_recvmsg+0xd6/0x150
[ 208.579490] do_syscall_64+0x5e/0x150
[ 208.579490] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 208.579490]
[ 208.579490] The buggy address belongs to the object at ffff88806c786d00
[ 208.579490] which belongs to the cache kmalloc-1k of size 1024
[ 208.579490] The buggy address is located 992 bytes inside of
[ 208.579490] 1024-byte region [ffff88806c786d00, ffff88806c787100)
[ 208.579490] The buggy address belongs to the page:
[ 208.579490] page:ffffea0001b1e100 count:1 mapcount:0 mapping:ffff88806d001140 index:0x0 compound_mapcount: 0
[ 208.579490] flags: 0x100000000010200(slab|head)
[ 208.579490] raw: 0100000000010200 ffffea0001b08e00 0000000300000003 ffff88806d001140
[ 208.579490] raw: 0000000000000000 00000000800e000e 00000001ffffffff 0000000000000000
[ 208.579490] page dumped because: kasan: bad access detected
[ 208.579490]
[ 208.579490] Memory state around the buggy address:
[ 208.579490] ffff88806c786f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 208.579490] ffff88806c787000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 208.579490] >ffff88806c787080: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc
[ 208.579490] ^
[ 208.579490] ffff88806c787100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 208.579490] ffff88806c787180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 208.579490] ==================================================================
Conclusion
Mounting Crafted image can cause slab-out-of-bounds in init_min_max_mtime 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.