[PATCH v2 2/2] f2fs: fix to do sanity check with extra_attr feature
From: Chao Yu
Date: Mon Jun 25 2018 - 11:30:36 EST
From: Chao Yu <yuchao0@xxxxxxxxxx>
If FI_EXTRA_ATTR is set in inode by fuzzing, inode.i_addr[0] will be
parsed as inode.i_extra_isize, then in __recover_inline_status, inline
data address will beyond boundary of page, result in accessing invalid
memory.
So in this condition, during reading inode page, let's do sanity check
with EXTRA_ATTR feature of fs and extra_attr bit of inode, if they're
inconsistent, deny to load this inode.
- Overview
Out-of-bound access in f2fs_iget() when mounting a corrupted f2fs image
- Reproduce
The following message will be got in KASAN build of 4.18 upstream kernel.
[ 819.392227] ==================================================================
[ 819.393901] BUG: KASAN: slab-out-of-bounds in f2fs_iget+0x736/0x1530
[ 819.395329] Read of size 4 at addr ffff8801f099c968 by task mount/1292
[ 819.397079] CPU: 1 PID: 1292 Comm: mount Not tainted 4.18.0-rc1+ #4
[ 819.397082] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 819.397088] Call Trace:
[ 819.397124] dump_stack+0x7b/0xb5
[ 819.397154] print_address_description+0x70/0x290
[ 819.397159] kasan_report+0x291/0x390
[ 819.397163] ? f2fs_iget+0x736/0x1530
[ 819.397176] check_memory_region+0x139/0x190
[ 819.397182] __asan_loadN+0xf/0x20
[ 819.397185] f2fs_iget+0x736/0x1530
[ 819.397197] f2fs_fill_super+0x1b4f/0x2b40
[ 819.397202] ? f2fs_fill_super+0x1b4f/0x2b40
[ 819.397208] ? f2fs_commit_super+0x1b0/0x1b0
[ 819.397227] ? set_blocksize+0x90/0x140
[ 819.397241] mount_bdev+0x1c5/0x210
[ 819.397245] ? f2fs_commit_super+0x1b0/0x1b0
[ 819.397252] f2fs_mount+0x15/0x20
[ 819.397256] mount_fs+0x60/0x1a0
[ 819.397267] ? alloc_vfsmnt+0x309/0x360
[ 819.397272] vfs_kern_mount+0x6b/0x1a0
[ 819.397282] do_mount+0x34a/0x18c0
[ 819.397300] ? lockref_put_or_lock+0xcf/0x160
[ 819.397306] ? copy_mount_string+0x20/0x20
[ 819.397318] ? memcg_kmem_put_cache+0x1b/0xa0
[ 819.397324] ? kasan_check_write+0x14/0x20
[ 819.397334] ? _copy_from_user+0x6a/0x90
[ 819.397353] ? memdup_user+0x42/0x60
[ 819.397359] ksys_mount+0x83/0xd0
[ 819.397365] __x64_sys_mount+0x67/0x80
[ 819.397388] do_syscall_64+0x78/0x170
[ 819.397403] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 819.397422] RIP: 0033:0x7f54c667cb9a
[ 819.397424] Code: 48 8b 0d 01 c3 2b 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 ce c2 2b 00 f7 d8 64 89 01 48
[ 819.397483] RSP: 002b:00007ffd8f46cd08 EFLAGS: 00000202 ORIG_RAX: 00000000000000a5
[ 819.397496] RAX: ffffffffffffffda RBX: 0000000000dfa030 RCX: 00007f54c667cb9a
[ 819.397498] RDX: 0000000000dfa210 RSI: 0000000000dfbf30 RDI: 0000000000e02ec0
[ 819.397501] RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000013
[ 819.397503] R10: 00000000c0ed0000 R11: 0000000000000202 R12: 0000000000e02ec0
[ 819.397505] R13: 0000000000dfa210 R14: 0000000000000000 R15: 0000000000000003
[ 819.397866] Allocated by task 139:
[ 819.398702] save_stack+0x46/0xd0
[ 819.398705] kasan_kmalloc+0xad/0xe0
[ 819.398709] kasan_slab_alloc+0x11/0x20
[ 819.398713] kmem_cache_alloc+0xd1/0x1e0
[ 819.398717] dup_fd+0x50/0x4c0
[ 819.398740] copy_process.part.37+0xbed/0x32e0
[ 819.398744] _do_fork+0x16e/0x590
[ 819.398748] __x64_sys_clone+0x69/0x80
[ 819.398752] do_syscall_64+0x78/0x170
[ 819.398756] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 819.399097] Freed by task 159:
[ 819.399743] save_stack+0x46/0xd0
[ 819.399747] __kasan_slab_free+0x13c/0x1a0
[ 819.399750] kasan_slab_free+0xe/0x10
[ 819.399754] kmem_cache_free+0x89/0x1e0
[ 819.399757] put_files_struct+0x132/0x150
[ 819.399761] exit_files+0x62/0x70
[ 819.399766] do_exit+0x47b/0x1390
[ 819.399770] do_group_exit+0x86/0x130
[ 819.399774] __x64_sys_exit_group+0x2c/0x30
[ 819.399778] do_syscall_64+0x78/0x170
[ 819.399782] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 819.400115] The buggy address belongs to the object at ffff8801f099c680
which belongs to the cache files_cache of size 704
[ 819.403234] The buggy address is located 40 bytes to the right of
704-byte region [ffff8801f099c680, ffff8801f099c940)
[ 819.405689] The buggy address belongs to the page:
[ 819.406709] page:ffffea0007c26700 count:1 mapcount:0 mapping:ffff8801f69a3340 index:0xffff8801f099d380 compound_mapcount: 0
[ 819.408984] flags: 0x2ffff0000008100(slab|head)
[ 819.409932] raw: 02ffff0000008100 ffffea00077fb600 0000000200000002 ffff8801f69a3340
[ 819.411514] raw: ffff8801f099d380 0000000080130000 00000001ffffffff 0000000000000000
[ 819.413073] page dumped because: kasan: bad access detected
[ 819.414539] Memory state around the buggy address:
[ 819.415521] ffff8801f099c800: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 819.416981] ffff8801f099c880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 819.418454] >ffff8801f099c900: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
[ 819.419921] ^
[ 819.421265] ffff8801f099c980: fc fc fc fc fc fc fc fc fb fb fb fb fb fb fb fb
[ 819.422745] ffff8801f099ca00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 819.424206] ==================================================================
[ 819.425668] Disabling lock debugging due to kernel taint
[ 819.457463] F2FS-fs (loop0): Mounted with checkpoint version = 3
The kernel still mounts the image. If you run the following program on the mounted folder mnt,
(poc.c)
static void activity(char *mpoint) {
char *foo_bar_baz;
int err;
static int buf[8192];
memset(buf, 0, sizeof(buf));
err = asprintf(&foo_bar_baz, "%s/foo/bar/baz", mpoint);
int fd = open(foo_bar_baz, O_RDONLY, 0);
if (fd >= 0) {
read(fd, (char *)buf, 11);
close(fd);
}
}
int main(int argc, char *argv[]) {
activity(argv[1]);
return 0;
}
You can get kernel crash:
[ 819.457463] F2FS-fs (loop0): Mounted with checkpoint version = 3
[ 918.028501] BUG: unable to handle kernel paging request at ffffed0048000d82
[ 918.044020] PGD 23ffee067 P4D 23ffee067 PUD 23fbef067 PMD 0
[ 918.045207] Oops: 0000 [#1] SMP KASAN PTI
[ 918.046048] CPU: 0 PID: 1309 Comm: poc Tainted: G B 4.18.0-rc1+ #4
[ 918.047573] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 918.049552] RIP: 0010:check_memory_region+0x5e/0x190
[ 918.050565] Code: f8 49 c1 e8 03 49 89 db 49 c1 eb 03 4d 01 cb 4d 01 c1 4d 8d 63 01 4c 89 c8 4d 89 e2 4d 29 ca 49 83 fa 10 7f 3d 4d 85 d2 74 32 <41> 80 39 00 75 23 48 b8 01 00 00 00 00 fc ff df 4d 01 d1 49 01 c0
[ 918.054322] RSP: 0018:ffff8801e3a1f258 EFLAGS: 00010202
[ 918.055400] RAX: ffffed0048000d82 RBX: ffff880240006c11 RCX: ffffffffb8867d14
[ 918.056832] RDX: 0000000000000000 RSI: 0000000000000002 RDI: ffff880240006c10
[ 918.058253] RBP: ffff8801e3a1f268 R08: 1ffff10048000d82 R09: ffffed0048000d82
[ 918.059717] R10: 0000000000000001 R11: ffffed0048000d82 R12: ffffed0048000d83
[ 918.061159] R13: ffff8801e3a1f390 R14: 0000000000000000 R15: ffff880240006c08
[ 918.062614] FS: 00007fac9732c700(0000) GS:ffff8801f6e00000(0000) knlGS:0000000000000000
[ 918.064246] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 918.065412] CR2: ffffed0048000d82 CR3: 00000001df77a000 CR4: 00000000000006f0
[ 918.066882] Call Trace:
[ 918.067410] __asan_loadN+0xf/0x20
[ 918.068149] f2fs_find_target_dentry+0xf4/0x270
[ 918.069083] ? __get_node_page+0x331/0x5b0
[ 918.069925] f2fs_find_in_inline_dir+0x24b/0x310
[ 918.070881] ? f2fs_recover_inline_data+0x4c0/0x4c0
[ 918.071905] ? unwind_next_frame.part.5+0x34f/0x490
[ 918.072901] ? unwind_dump+0x290/0x290
[ 918.073695] ? is_bpf_text_address+0xe/0x20
[ 918.074566] __f2fs_find_entry+0x599/0x670
[ 918.075408] ? kasan_unpoison_shadow+0x36/0x50
[ 918.076315] ? kasan_kmalloc+0xad/0xe0
[ 918.077100] ? memcg_kmem_put_cache+0x55/0xa0
[ 918.077998] ? f2fs_find_target_dentry+0x270/0x270
[ 918.079006] ? d_set_d_op+0x30/0x100
[ 918.079749] ? __d_lookup_rcu+0x69/0x2e0
[ 918.080556] ? __d_alloc+0x275/0x450
[ 918.081297] ? kasan_check_write+0x14/0x20
[ 918.082135] ? memset+0x31/0x40
[ 918.082820] ? fscrypt_setup_filename+0x1ec/0x4c0
[ 918.083782] ? d_alloc_parallel+0x5bb/0x8c0
[ 918.084640] f2fs_find_entry+0xe9/0x110
[ 918.085432] ? __f2fs_find_entry+0x670/0x670
[ 918.086308] ? kasan_check_write+0x14/0x20
[ 918.087163] f2fs_lookup+0x297/0x590
[ 918.087902] ? f2fs_link+0x2b0/0x2b0
[ 918.088646] ? legitimize_path.isra.29+0x61/0xa0
[ 918.089589] __lookup_slow+0x12e/0x240
[ 918.090371] ? may_delete+0x2b0/0x2b0
[ 918.091123] ? __nd_alloc_stack+0xa0/0xa0
[ 918.091944] lookup_slow+0x44/0x60
[ 918.092642] walk_component+0x3ee/0xa40
[ 918.093428] ? is_bpf_text_address+0xe/0x20
[ 918.094283] ? pick_link+0x3e0/0x3e0
[ 918.095047] ? in_group_p+0xa5/0xe0
[ 918.095771] ? generic_permission+0x53/0x1e0
[ 918.096666] ? security_inode_permission+0x1d/0x70
[ 918.097646] ? inode_permission+0x7a/0x1f0
[ 918.098497] link_path_walk+0x2a2/0x7b0
[ 918.099298] ? apparmor_capget+0x3d0/0x3d0
[ 918.100140] ? walk_component+0xa40/0xa40
[ 918.100958] ? path_init+0x2e6/0x580
[ 918.101695] path_openat+0x1bb/0x2160
[ 918.102471] ? __save_stack_trace+0x92/0x100
[ 918.103352] ? save_stack+0xb5/0xd0
[ 918.104070] ? vfs_unlink+0x250/0x250
[ 918.104822] ? save_stack+0x46/0xd0
[ 918.105538] ? kasan_slab_alloc+0x11/0x20
[ 918.106370] ? kmem_cache_alloc+0xd1/0x1e0
[ 918.107213] ? getname_flags+0x76/0x2c0
[ 918.107997] ? getname+0x12/0x20
[ 918.108677] ? do_sys_open+0x14b/0x2c0
[ 918.109450] ? __x64_sys_open+0x4c/0x60
[ 918.110255] ? do_syscall_64+0x78/0x170
[ 918.111083] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 918.112148] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 918.113204] ? f2fs_empty_inline_dir+0x1e0/0x1e0
[ 918.114150] ? timespec64_trunc+0x5c/0x90
[ 918.114993] ? wb_io_lists_depopulated+0x1a/0xc0
[ 918.115937] ? inode_io_list_move_locked+0x102/0x110
[ 918.116949] do_filp_open+0x12b/0x1d0
[ 918.117709] ? may_open_dev+0x50/0x50
[ 918.118475] ? kasan_kmalloc+0xad/0xe0
[ 918.119246] do_sys_open+0x17c/0x2c0
[ 918.119983] ? do_sys_open+0x17c/0x2c0
[ 918.120751] ? filp_open+0x60/0x60
[ 918.121463] ? task_work_run+0x4d/0xf0
[ 918.122237] __x64_sys_open+0x4c/0x60
[ 918.123001] do_syscall_64+0x78/0x170
[ 918.123759] entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 918.124802] RIP: 0033:0x7fac96e3e040
[ 918.125537] Code: 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 83 3d 09 27 2d 00 00 75 10 b8 02 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 31 c3 48 83 ec 08 e8 7e e0 01 00 48 89 04 24
[ 918.129341] RSP: 002b:00007fff1b37f848 EFLAGS: 00000246 ORIG_RAX: 0000000000000002
[ 918.130870] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fac96e3e040
[ 918.132295] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 000000000122d080
[ 918.133748] RBP: 00007fff1b37f9b0 R08: 00007fac9710bbd8 R09: 0000000000000001
[ 918.135209] R10: 000000000000069d R11: 0000000000000246 R12: 0000000000400c20
[ 918.136650] R13: 00007fff1b37fab0 R14: 0000000000000000 R15: 0000000000000000
[ 918.138093] Modules linked in: snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd mac_hid i2c_piix4 soundcore ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx raid1 raid0 multipath linear 8139too crct10dif_pclmul crc32_pclmul qxl drm_kms_helper syscopyarea aesni_intel sysfillrect sysimgblt fb_sys_fops ttm drm aes_x86_64 crypto_simd cryptd 8139cp glue_helper mii pata_acpi floppy
[ 918.147924] CR2: ffffed0048000d82
[ 918.148619] ---[ end trace 4ce02f25ff7d3df5 ]---
[ 918.149563] RIP: 0010:check_memory_region+0x5e/0x190
[ 918.150576] Code: f8 49 c1 e8 03 49 89 db 49 c1 eb 03 4d 01 cb 4d 01 c1 4d 8d 63 01 4c 89 c8 4d 89 e2 4d 29 ca 49 83 fa 10 7f 3d 4d 85 d2 74 32 <41> 80 39 00 75 23 48 b8 01 00 00 00 00 fc ff df 4d 01 d1 49 01 c0
[ 918.154360] RSP: 0018:ffff8801e3a1f258 EFLAGS: 00010202
[ 918.155411] RAX: ffffed0048000d82 RBX: ffff880240006c11 RCX: ffffffffb8867d14
[ 918.156833] RDX: 0000000000000000 RSI: 0000000000000002 RDI: ffff880240006c10
[ 918.158257] RBP: ffff8801e3a1f268 R08: 1ffff10048000d82 R09: ffffed0048000d82
[ 918.159722] R10: 0000000000000001 R11: ffffed0048000d82 R12: ffffed0048000d83
[ 918.161149] R13: ffff8801e3a1f390 R14: 0000000000000000 R15: ffff880240006c08
[ 918.162587] FS: 00007fac9732c700(0000) GS:ffff8801f6e00000(0000) knlGS:0000000000000000
[ 918.164203] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 918.165356] CR2: ffffed0048000d82 CR3: 00000001df77a000 CR4: 00000000000006f0
Reported-by: Wen Xu <wen.xu@xxxxxxxxxx>
Signed-off-by: Chao Yu <yuchao0@xxxxxxxxxx>
---
v2: fix to add missing parameter in format string.
fs/f2fs/inode.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
index 708f6722e574..e3fc1c046dea 100644
--- a/fs/f2fs/inode.c
+++ b/fs/f2fs/inode.c
@@ -202,6 +202,16 @@ static bool sanity_check_inode(struct inode *inode)
__func__, inode->i_ino);
return false;
}
+
+ if (f2fs_has_extra_attr(inode) &&
+ !f2fs_sb_has_extra_attr(sbi->sb)) {
+ set_sbi_flag(sbi, SBI_NEED_FSCK);
+ f2fs_msg(sbi->sb, KERN_WARNING,
+ "%s: inode (ino=%lx) is with extra_attr, "
+ "but extra_attr feature is off",
+ __func__, inode->i_ino);
+ return false;
+ }
return true;
}
@@ -254,6 +264,11 @@ static int do_read_inode(struct inode *inode)
get_inline_info(inode, ri);
+ if (!sanity_check_inode(inode)) {
+ f2fs_put_page(node_page, 1);
+ return -EINVAL;
+ }
+
fi->i_extra_isize = f2fs_has_extra_attr(inode) ?
le16_to_cpu(ri->i_extra_isize) : 0;
@@ -335,10 +350,6 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
ret = do_read_inode(inode);
if (ret)
goto bad_inode;
- if (!sanity_check_inode(inode)) {
- ret = -EINVAL;
- goto bad_inode;
- }
make_now:
if (ino == F2FS_NODE_INO(sbi)) {
inode->i_mapping->a_ops = &f2fs_node_aops;
--
2.16.2.17.g38e79b1fd