Bug 199181

Summary: Out-of-bound access in ext4_valid_block_bitmap when mounting and operating on 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.15.x Subsystem:
Regression: No Bisected commit-id:
Attachments: The crafted image which causes kernel panic
poc.c

Description Wen Xu 2018-03-22 20:29:12 UTC
Created attachment 274877 [details]
The crafted image which causes kernel panic

- Overview
Kernel crashes in ext4_valid_block_bitmap when mounting and operating a crafted ext4 image

- Reproduce (under root)
# mkdir mnt
# mount -t ext4 230.img mnt
# gcc -o poc poc.c
# ./poc mnt

- Reason
https://elixir.bootlin.com/linux/v4.15/source/fs/ext4/balloc.c#L343

if (!ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
    /* bad block bitmap */
    return blk;

static inline int test_bit_le(int nr, const void *addr)
{
    return test_bit(nr ^ BITOP_LE_SWIZZLE, addr);
}

static inline int test_bit(int nr, const volatile unsigned long *addr)
{
  return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
}

It seems kernel misses boundary check on BIT_WORD(nr), which causes out-of-bound access on addr (bh->b_data).

- Kernel crash dump
[  262.162847] EXT4-fs (loop0): barriers disabled
[  262.167094] EXT4-fs (loop0): warning: mounting fs with errors, running e2fsck is recommended
[  262.167268] EXT4-fs (loop0): recovery complete
[  262.167271] EXT4-fs (loop0): mounted filesystem with ordered data mode. Opts: (null)
[  267.285863] EXT4-fs error (device loop0): ext4_readdir:183: inode #2: comm poc: path /home/wen/poc/mnt: directory contains a hole at offset 1024
[  267.286057] BUG: unable to handle kernel paging request at ffff8b4bc81dc7f8
[  267.286095] IP: ext4_validate_block_bitmap+0x196/0x350
[  267.286116] PGD 1b13f067 P4D 1b13f067 PUD 0
[  267.286137] Oops: 0000 [#1] SMP PTI
[  267.286160] Modules linked in: vmw_balloon coretemp intel_rapl_perf input_leds joydev serio_raw snd_ens1371 btusb snd_ac97_codec uvcvideo btrtl videobuf2_vmalloc btbcm btintel gameport snd_rawmidi videobuf2_memops bluetooth videobuf2_v4l2 videobuf2_core snd_seq_device ac97_bus snd_pcm videodev media ecdh_generic snd_timer snd soundcore shpchp mac_hid vmw_vsock_vmci_transport vsock vmw_vmci sch_fq_codel ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi ip_tables x_tables autofs4 btrfs zstd_compress raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid1 raid0 multipath linear hid_generic usbhid hid crct10dif_pclmul crc32_pclmul ghash_clmulni_intel pcbc aesni_intel aes_x86_64 crypto_simd glue_helper cryptd vmwgfx
[  267.286543]  psmouse ttm drm_kms_helper mptspi mptscsih ahci libahci e1000 mptbase scsi_transport_spi syscopyarea sysfillrect sysimgblt fb_sys_fops drm i2c_piix4 pata_acpi
[  267.286650] CPU: 0 PID: 1344 Comm: poc Not tainted 4.15.0-12-generic #13-Ubuntu
[  267.286679] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015
[  267.286727] RIP: 0010:ext4_validate_block_bitmap+0x196/0x350
[  267.286751] RSP: 0018:ffffb90741703868 EFLAGS: 00010202
[  267.286775] RAX: 000000007ffffffe RBX: ffff8b4bb91a1000 RCX: 0000000000000000
[  267.286833] RDX: ffff8b4bb81dc800 RSI: ffff8b4bb81dc800 RDI: ffff8b4bb91a1000
[  267.286862] RBP: ffffb907417038b8 R08: ffff8b4bb91a2000 R09: 000000007fffffff
[  267.286889] R10: ffff8b4bb81dc900 R11: ffffb90741703798 R12: 0000000000000000
[  267.286917] R13: ffff8b4bb8e1e2d8 R14: 0000000000000000 R15: ffff8b4bb91a2000
[  267.286946] FS:  00007fc90862d540(0000) GS:ffff8b4bbc600000(0000) knlGS:0000000000000000
[  267.286976] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  267.287001] CR2: ffff8b4bc81dc7f8 CR3: 0000000038210006 CR4: 00000000001606f0
[  267.287061] Call Trace:
[  267.287082]  ? ext4_bg_num_gdb+0x7c/0x90
[  267.287106]  ext4_read_block_bitmap_nowait+0xa5/0x5e0
[  267.287129]  ext4_read_block_bitmap+0x19/0x50
[  267.287712]  ext4_free_blocks+0x1f1/0xb60
[  267.288250]  ? _cond_resched+0x19/0x40
[  267.288785]  ? __kmalloc+0x19b/0x220
[  267.289265]  ext4_ext_remove_space+0xb35/0x1200
[  267.289732]  ext4_ext_truncate+0x88/0xa0
[  267.290127]  ext4_truncate+0x363/0x400
[  267.290514]  ext4_setattr+0x95e/0x9d0
[  267.290935]  notify_change+0x2eb/0x440
[  267.291322]  ? ext4_xattr_security_set+0x30/0x30
[  267.291703]  do_truncate+0x73/0xc0
[  267.292071]  path_openat+0xf84/0x16c0
[  267.292426]  ? touch_atime+0x36/0xe0
[  267.292769]  do_filp_open+0x9b/0x110
[  267.293102]  ? __check_object_size+0xaf/0x1b0
[  267.293427]  ? __alloc_fd+0x46/0x170
[  267.293742]  do_sys_open+0x1bb/0x2c0
[  267.294042]  ? do_sys_open+0x1bb/0x2c0
[  267.294332]  ? _cond_resched+0x19/0x40
[  267.294660]  SyS_openat+0x14/0x20
[  267.294941]  do_syscall_64+0x73/0x130
[  267.295208]  entry_SYSCALL_64_after_hwframe+0x3d/0xa2
[  267.295472] RIP: 0033:0x7fc90812ec8e
[  267.295723] RSP: 002b:00007ffc8c52e940 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
[  267.295979] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007fc90812ec8e
[  267.296237] RDX: 0000000000000202 RSI: 000055ed620a42d0 RDI: 00000000ffffff9c
[  267.296495] RBP: 00007ffc8c52eb10 R08: 0000000000000003 R09: 0000000000000000
[  267.296754] R10: 0000000000000000 R11: 0000000000000246 R12: 000055ed61d01d30
[  267.297010] R13: 00007ffc8c52ec10 R14: 0000000000000000 R15: 0000000000000000
[  267.297267] Code: 10 48 8b 75 d0 4c 89 45 b8 48 01 d0 48 89 45 c8 e8 d0 dc 04 00 4c 8b 45 b8 49 89 c1 2b 45 c8 49 8b 55 28 41 8b 48 54 d3 f8 48 98 <48> 0f a3 02 72 7b 4d 85 c9 0f 84 62 ff ff ff 48 8b 83 08 04 00
[  267.298075] RIP: ext4_validate_block_bitmap+0x196/0x350 RSP: ffffb90741703868
[  267.298347] CR2: ffff8b4bc81dc7f8
[  267.298613] ---[ end trace 1ecf08f3cdf242f0 ]--- 

Reported by Wen Xu from SSlab, Gatech
Comment 1 Wen Xu 2018-03-22 20:30:17 UTC
Created attachment 274879 [details]
poc.c
Comment 2 Wen Xu 2018-03-22 20:31:11 UTC
By the way, the dump is generated on latest Ubuntu 18.04 which uses Linux 4.15.
Comment 3 Theodore Tso 2018-03-24 06:30:58 UTC
Thanks for the bug report.  The following should address the problem
you have reported.

commit 25584a8748b1f5404abcdc87c9fac21681eec5d2
Author: Theodore Ts'o <tytso@mit.edu>
Date:   Sat Mar 24 01:48:34 2018 -0400

    ext4: add validity checks for bitmap block numbers
    
    https://bugzilla.kernel.org/show_bug.cgi?id=199181
    
    Reported-by: Wen Xu <wen.xu@gatech.edu>
    Signed-off-by: Theodore Ts'o <tytso@mit.edu>
    Cc: stable@vger.kernel.org

diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index f82c4966f4ce..a33d8fb1bf2a 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -338,20 +338,25 @@ static ext4_fsblk_t ext4_valid_block_bitmap(struct super_block *sb,
 	/* check whether block bitmap block number is set */
 	blk = ext4_block_bitmap(sb, desc);
 	offset = blk - group_first_block;
-	if (!ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
+	if (offset < 0 || EXT4_B2C(sbi, offset) >= sb->s_blocksize ||
+	    !ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
 		/* bad block bitmap */
 		return blk;
 
 	/* check whether the inode bitmap block number is set */
 	blk = ext4_inode_bitmap(sb, desc);
 	offset = blk - group_first_block;
-	if (!ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
+	if (offset < 0 || EXT4_B2C(sbi, offset) >= sb->s_blocksize ||
+	    !ext4_test_bit(EXT4_B2C(sbi, offset), bh->b_data))
 		/* bad block bitmap */
 		return blk;
 
 	/* check whether the inode table block number is set */
 	blk = ext4_inode_table(sb, desc);
 	offset = blk - group_first_block;
+	if (offset < 0 || EXT4_B2C(sbi, offset) >= sb->s_blocksize ||
+	    EXT4_B2C(sbi, offset + sbi->s_itb_per_group) >= sb->s_blocksize)
+		return blk;
 	next_zero_bit = ext4_find_next_zero_bit(bh->b_data,
 			EXT4_B2C(sbi, offset + sbi->s_itb_per_group),
 			EXT4_B2C(sbi, offset));
@@ -417,6 +422,7 @@ struct buffer_head *
 ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
 {
 	struct ext4_group_desc *desc;
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
 	struct buffer_head *bh;
 	ext4_fsblk_t bitmap_blk;
 	int err;
@@ -425,6 +431,12 @@ ext4_read_block_bitmap_nowait(struct super_block *sb, ext4_group_t block_group)
 	if (!desc)
 		return ERR_PTR(-EFSCORRUPTED);
 	bitmap_blk = ext4_block_bitmap(sb, desc);
+	if ((bitmap_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
+	    (bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
+		ext4_error(sb, "Invalid block bitmap block %llu in "
+			   "block_group %u", bitmap_blk, block_group);
+		return ERR_PTR(-EFSCORRUPTED);
+	}
 	bh = sb_getblk(sb, bitmap_blk);
 	if (unlikely(!bh)) {
 		ext4_error(sb, "Cannot get buffer for block bitmap - "
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c
index 3fa93665b4a3..df92e3ec9913 100644
--- a/fs/ext4/ialloc.c
+++ b/fs/ext4/ialloc.c
@@ -122,6 +122,7 @@ static struct buffer_head *
 ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
 {
 	struct ext4_group_desc *desc;
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
 	struct buffer_head *bh = NULL;
 	ext4_fsblk_t bitmap_blk;
 	int err;
@@ -131,6 +132,12 @@ ext4_read_inode_bitmap(struct super_block *sb, ext4_group_t block_group)
 		return ERR_PTR(-EFSCORRUPTED);
 
 	bitmap_blk = ext4_inode_bitmap(sb, desc);
+	if ((bitmap_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) ||
+	    (bitmap_blk >= ext4_blocks_count(sbi->s_es))) {
+		ext4_error(sb, "Invalid inode bitmap blk %llu in "
+			   "block_group %u", bitmap_blk, block_group);
+		return ERR_PTR(-EFSCORRUPTED);
+	}
 	bh = sb_getblk(sb, bitmap_blk);
 	if (unlikely(!bh)) {
 		ext4_error(sb, "Cannot read inode bitmap - "