Bug 200933 - Divide zero in __ext4_check_dir_entry
Summary: Divide zero in __ext4_check_dir_entry
Status: ASSIGNED
Alias: None
Product: File System
Classification: Unclassified
Component: ext4 (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: fs_ext4@kernel-bugs.osdl.org
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-08-25 05:14 UTC by Wen Xu
Modified: 2018-08-31 17:39 UTC (History)
2 users (show)

See Also:
Kernel Version: 4.18
Subsystem:
Regression: No
Bisected commit-id:


Attachments
The (compressed) crafted image which causes crash (20.46 KB, application/zip)
2018-08-25 05:14 UTC, Wen Xu
Details
poc (1.46 KB, text/x-csrc)
2018-08-25 05:14 UTC, Wen Xu
Details
Simplified crafted image (19.16 KB, application/gzip)
2018-08-27 13:37 UTC, Theodore Tso
Details

Description Wen Xu 2018-08-25 05:14:33 UTC
Created attachment 278081 [details]
The (compressed) crafted image which causes crash

- Reproduce
# mkdir mnt
# mount -t ext4 17.img mnt
# gcc 17.c
# ./a.out ./mnt

- Kernel message
[ 6687.218549] EXT4-fs: Warning: mounting with data=journal disables delayed allocation and O_DIRECT support!
[ 6687.289272] [EXT4 FS bs=1024, gc=2, bpg=8192, ipg=2048, mo=a002c41c, mo2=0002]
[ 6687.289342] System zones: 1-2, 130-1157, 8193-8194
[ 6687.289710] EXT4-fs (loop0): mounted filesystem with journalled data mode. Opts: (null)
[ 6705.578120] divide error: 0000 [#1] SMP KASAN PTI
[ 6705.579152] CPU: 1 PID: 1590 Comm: a.out Not tainted 4.18.0+ #9
[ 6705.580379] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 6705.582323] RIP: 0010:__ext4_check_dir_entry+0x1aa/0x200
[ 6705.583411] Code: 18 e8 ca b1 e7 ff 48 8b 45 c8 31 d2 48 8b 75 c0 4d 89 f9 49 c7 c0 e0 95 94 a6 4c 89 e7 48 8b 48 18 8b 45 20 41 55 53 41 56 50 <f7> 75 18 52 8b 55 bc e8 6a 72 08 00 b8 01 00 00 00 48 83 c4 28 e9
[ 6705.587100] RSP: 0018:ffff8801f0ae7ac0 EFLAGS: 00010246
[ 6705.588177] RAX: 000000000000003c RBX: 000000000000000c RCX: 0000000000000286
[ 6705.589606] RDX: 0000000000000000 RSI: ffffffffa694cc00 RDI: ffff8801d1e2b478
[ 6705.591027] RBP: ffff8801f0ae7b28 R08: ffffffffa69495e0 R09: ffffffffa6949560
[ 6705.592458] R10: 0000000000000001 R11: ffffed003d690e36 R12: ffff8801d1e2b478
[ 6705.593878] R13: 0000000000000004 R14: 0000000000000011 R15: ffffffffa6949560
[ 6705.595306] FS:  00007f6837cae700(0000) GS:ffff8801f7100000(0000) knlGS:0000000000000000
[ 6705.596934] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 6705.598089] CR2: 00007f68377c0a90 CR3: 00000001e52c4000 CR4: 00000000000006e0
[ 6705.599538] Call Trace:
[ 6705.600070]  empty_inline_dir+0x22c/0x420
[ 6705.600897]  ? ext4_delete_inline_entry+0x300/0x300
[ 6705.601888]  ? __dquot_initialize+0xeb/0x5d0
[ 6705.602776]  ? unlazy_walk+0xb8/0x150
[ 6705.603543]  ext4_empty_dir+0x1dd/0x420
[ 6705.604344]  ? apparmor_file_receive+0x80/0x80
[ 6705.605245]  ? ext4_mkdir+0x6b0/0x6b0
[ 6705.606001]  ext4_rmdir+0x1c2/0x750
[ 6705.606719]  ? ext4_rename2+0x100/0x100
[ 6705.607529]  ? lockref_get_not_dead+0x160/0x160
[ 6705.608451]  ? may_delete+0x206/0x2b0
[ 6705.609201]  vfs_rmdir+0x104/0x1b0
[ 6705.609902]  do_rmdir+0x308/0x330
[ 6705.610587]  ? __ia32_sys_mkdir+0x40/0x40
[ 6705.611424]  ? do_faccessat+0x303/0x390
[ 6705.612212]  ? __ia32_sys_fallocate+0x60/0x60
[ 6705.613104]  ? getname_flags+0x110/0x2c0
[ 6705.613910]  __x64_sys_rmdir+0x24/0x30
[ 6705.614684]  do_syscall_64+0x78/0x170
[ 6705.615457]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[ 6705.616489] RIP: 0033:0x7f68377ca4d9
[ 6705.617223] 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 8f 29 2c 00 f7 d8 64 89 01 48
[ 6705.620927] RSP: 002b:00007ffe90ddedd8 EFLAGS: 00000286 ORIG_RAX: 0000000000000054
[ 6705.622442] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f68377ca4d9
[ 6705.623872] RDX: ffffffffffffff98 RSI: 00007f68377ca4d9 RDI: 00007ffe90ddee20
[ 6705.625294] RBP: 00007ffe90de0ed0 R08: 00007ffe90de0fb8 R09: 00007ffe90de0fb8
[ 6705.626721] R10: 00007ffe90de0fb8 R11: 0000000000000286 R12: 0000000000400530
[ 6705.628156] R13: 00007ffe90de0fb0 R14: 0000000000000000 R15: 0000000000000000
[ 6705.629591] Modules linked in: snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd soundcore mac_hid i2c_piix4 ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi autofs4 raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx raid1 raid0 multipath linear 8139too qxl drm_kms_helper syscopyarea sysfillrect sysimgblt fb_sys_fops ttm crct10dif_pclmul crc32_pclmul aesni_intel drm aes_x86_64 crypto_simd cryptd glue_helper 8139cp mii pata_acpi floppy
[ 6705.640196] ---[ end trace 754084f7e4b34756 ]---
[ 6705.641206] RIP: 0010:__ext4_check_dir_entry+0x1aa/0x200
[ 6705.642309] Code: 18 e8 ca b1 e7 ff 48 8b 45 c8 31 d2 48 8b 75 c0 4d 89 f9 49 c7 c0 e0 95 94 a6 4c 89 e7 48 8b 48 18 8b 45 20 41 55 53 41 56 50 <f7> 75 18 52 8b 55 bc e8 6a 72 08 00 b8 01 00 00 00 48 83 c4 28 e9
[ 6705.647143] RSP: 0018:ffff8801f0ae7ac0 EFLAGS: 00010246
[ 6705.648662] RAX: 000000000000003c RBX: 000000000000000c RCX: 0000000000000286
[ 6705.650450] RDX: 0000000000000000 RSI: ffffffffa694cc00 RDI: ffff8801d1e2b478
[ 6705.652001] RBP: ffff8801f0ae7b28 R08: ffffffffa69495e0 R09: ffffffffa6949560
[ 6705.653462] R10: 0000000000000001 R11: ffffed003d690e36 R12: ffff8801d1e2b478
[ 6705.654909] R13: 0000000000000004 R14: 0000000000000011 R15: ffffffffa6949560
[ 6705.656394] FS:  00007f6837cae700(0000) GS:ffff8801f7100000(0000) knlGS:0000000000000000
[ 6705.658025] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 6705.659188] CR2: 00007f68377c0a90 CR3: 00000001e52c4000 CR4: 00000000000006e0
[ 6705.660691] ==================================================================
[ 6705.662169] BUG: KASAN: stack-out-of-bounds in arch_tlb_gather_mmu+0x21/0x170
[ 6705.663613] Write of size 8 at addr ffff8801f0ae7bc8 by task a.out/1590

[ 6705.665277] CPU: 1 PID: 1590 Comm: a.out Tainted: G      D           4.18.0+ #9
[ 6705.666751] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[ 6705.681196] Call Trace:
[ 6705.681780]  dump_stack+0x7b/0xb5
[ 6705.682469]  print_address_description+0x70/0x290
[ 6705.683439]  kasan_report+0x291/0x390
[ 6705.684193]  ? arch_tlb_gather_mmu+0x21/0x170
[ 6705.685074]  __asan_store8+0x57/0x90
[ 6705.685808]  arch_tlb_gather_mmu+0x21/0x170
[ 6705.686660]  tlb_gather_mmu+0x12/0x40
[ 6705.687431]  free_ldt_pgtables.part.2+0x90/0x110
[ 6705.688372]  ? map_ldt_struct+0x4d0/0x4d0
[ 6705.689202]  ? run_rebalance_domains+0x170/0x170
[ 6705.690136]  ? ext4_empty_dir+0x1dd/0x420
[ 6705.690959]  ? __schedule+0x701/0xd90
[ 6705.691811]  free_ldt_pgtables+0x13/0x20
[ 6705.692616]  ldt_arch_exit_mmap+0xe/0x10
[ 6705.693421]  exit_mmap+0xeb/0x290
[ 6705.694101]  ? __ia32_sys_munmap+0x50/0x50
[ 6705.694937]  ? ext4_rmdir+0x1c2/0x750
[ 6705.695704]  ? exit_aio+0x98/0x230
[ 6705.696406]  ? __x32_compat_sys_io_submit+0x260/0x260
[ 6705.697445]  ? taskstats_exit+0x1f4/0x640
[ 6705.698267]  ? kasan_check_read+0x11/0x20
[ 6705.699085]  ? mm_update_next_owner+0x322/0x380
[ 6705.700017]  mmput+0x8b/0x1d0
[ 6705.700633]  do_exit+0x472/0x13c0
[ 6705.701318]  ? mm_update_next_owner+0x380/0x380
[ 6705.702243]  ? __ia32_sys_fallocate+0x60/0x60
[ 6705.703127]  ? getname_flags+0x110/0x2c0
[ 6705.703943]  ? __x64_sys_rmdir+0x24/0x30
[ 6705.704749]  rewind_stack_do_exit+0x17/0x20
[ 6705.705619] RIP: 0033:0x7f68377ca4d9
[ 6705.706358] 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 8f 29 2c 00 f7 d8 64 89 01 48
[ 6705.710080] RSP: 002b:00007ffe90ddedd8 EFLAGS: 00000286 ORIG_RAX: 0000000000000054
[ 6705.711602] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f68377ca4d9
[ 6705.713025] RDX: ffffffffffffff98 RSI: 00007f68377ca4d9 RDI: 00007ffe90ddee20
[ 6705.714452] RBP: 00007ffe90de0ed0 R08: 00007ffe90de0fb8 R09: 00007ffe90de0fb8
[ 6705.715890] R10: 00007ffe90de0fb8 R11: 0000000000000286 R12: 0000000000400530
[ 6705.717313] R13: 00007ffe90de0fb0 R14: 0000000000000000 R15: 0000000000000000

[ 6705.719070] The buggy address belongs to the page:
[ 6705.720063] page:ffffea0007c2b9c0 count:0 mapcount:0 mapping:0000000000000000 index:0x0
[ 6705.721663] flags: 0x2ffff0000000000()
[ 6705.722424] raw: 02ffff0000000000 0000000000000000 ffffea0007c2b988 0000000000000000
[ 6705.723977] raw: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[ 6705.725517] page dumped because: kasan: bad access detected

[ 6705.726953] Memory state around the buggy address:
[ 6705.727933]  ffff8801f0ae7a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 6705.729388]  ffff8801f0ae7b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 6705.730828] >ffff8801f0ae7b80: 00 f1 f1 f1 f1 f1 f1 f1 f1 f3 f3 00 00 00 00 00
[ 6705.732277]                                               ^
[ 6705.733397]  ffff8801f0ae7c00: 00 00 00 00 00 00 00 00 00 00 f4 f4 f4 f3 f3 f3
[ 6705.734852]  ffff8801f0ae7c80: f3 f4 f3 f3 f3 f3 00 00 f1 f1 f1 f1 00 00 00 00
[ 6705.736325] ==================================================================

Reported by Wen Xu (wen.xu@gatech.edu) from SSLab
Comment 1 Wen Xu 2018-08-25 05:14:46 UTC
Created attachment 278083 [details]
poc
Comment 2 Theodore Tso 2018-08-27 13:37:07 UTC
Created attachment 278125 [details]
Simplified crafted image

Here is a simplified crafted image.   Here is also a simplified reproducer:

mount poc-200933.img.simplified /mnt
rmdir /mnt/foo/bar

The bug is in the function which checks to see if an inline directory is empty; it is relying on the i_size of the inline directory, and if that value is larger than what is correct, we can either overrun the buffer, or as in this case, trigger a division by zero error when we find that the size of the next "directory entry" is zero.   (This didn't trigger a KASAN error because it was still a legal part of the inode table block.)
Comment 3 Theodore Tso 2018-08-27 14:37:45 UTC
Patch to fix this:

http://patchwork.ozlabs.org/patch/962516/

Note: I will probably be retitling and rewriting the patch description, because on further reflection, it's not possible for this to become a buffer overrun attack.   That's because we are checking to make sure the inline directory does not exceed the bounds of the inline data xattr.  The size == 0 is coming from the "end of xattr list" marker in the xattr data structure.  That has to be there, or we would have declared the xattr data structure to be corrupt before we even tried to parse the inline directory.   So the only way the attacker can manifest a problem is by causing the divide by zero in __ext4_check_dir_entry().

So for CVE scoring purposes, this is just a denial of service attack (triggering a kernel divide by zero trap, which doesn't kill the kernel per se, but which leaves various locks and refcounts held, which will eventually cause the system to become wedged).  It is not a buffer overrun.
Comment 4 Theodore Tso 2018-08-31 17:31:15 UTC
Revised description:

ext4: avoid divide by zero fault when deleting corrupted inline directories

A specially crafted file system can trick empty_inline_dir() into
reading past the last valid entry in a inline directory, and then run
into the end of xattr marker. This will trigger a divide by zero
fault.  Fix this by using the size of the inline directory instead of
dir->i_size.

Also clean up error reporting in __ext4_check_dir_entry so that the
message is clearer and more understandable --- and avoids the division
by zero trap if the size passed in is zero.  (I'm not sure why we
coded it that way in the first place; printing offset % size is
actually more confusing and less useful.)

https://bugzilla.kernel.org/show_bug.cgi?id=200933

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reported-by: Wen Xu <wen.xu@gatech.edu>
Cc: stable@vger.kernel.org

Note You need to log in before you can comment on or make changes to this bug.