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
Created attachment 274879 [details] poc.c
By the way, the dump is generated on latest Ubuntu 18.04 which uses Linux 4.15.
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 - "