| From 8844618d8aa7a9973e7b527d038a2a589665002c Mon Sep 17 00:00:00 2001 |
| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Thu, 14 Jun 2018 00:58:00 -0400 |
| Subject: ext4: only look at the bg_flags field if it is valid |
| |
| From: Theodore Ts'o <tytso@mit.edu> |
| |
| commit 8844618d8aa7a9973e7b527d038a2a589665002c upstream. |
| |
| The bg_flags field in the block group descripts is only valid if the |
| uninit_bg or metadata_csum feature is enabled. We were not |
| consistently looking at this field; fix this. |
| |
| Also block group #0 must never have uninitialized allocation bitmaps, |
| or need to be zeroed, since that's where the root inode, and other |
| special inodes are set up. Check for these conditions and mark the |
| file system as corrupted if they are detected. |
| |
| This addresses CVE-2018-10876. |
| |
| https://bugzilla.kernel.org/show_bug.cgi?id=199403 |
| |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Cc: stable@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ext4/balloc.c | 11 ++++++++++- |
| fs/ext4/ialloc.c | 14 ++++++++++++-- |
| fs/ext4/mballoc.c | 6 ++++-- |
| fs/ext4/super.c | 11 ++++++++++- |
| 4 files changed, 36 insertions(+), 6 deletions(-) |
| |
| --- a/fs/ext4/balloc.c |
| +++ b/fs/ext4/balloc.c |
| @@ -450,7 +450,16 @@ ext4_read_block_bitmap_nowait(struct sup |
| goto verify; |
| } |
| ext4_lock_group(sb, block_group); |
| - if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { |
| + if (ext4_has_group_desc_csum(sb) && |
| + (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) { |
| + if (block_group == 0) { |
| + ext4_unlock_group(sb, block_group); |
| + unlock_buffer(bh); |
| + ext4_error(sb, "Block bitmap for bg 0 marked " |
| + "uninitialized"); |
| + err = -EFSCORRUPTED; |
| + goto out; |
| + } |
| err = ext4_init_block_bitmap(sb, bh, block_group, desc); |
| set_bitmap_uptodate(bh); |
| set_buffer_uptodate(bh); |
| --- a/fs/ext4/ialloc.c |
| +++ b/fs/ext4/ialloc.c |
| @@ -152,7 +152,16 @@ ext4_read_inode_bitmap(struct super_bloc |
| } |
| |
| ext4_lock_group(sb, block_group); |
| - if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { |
| + if (ext4_has_group_desc_csum(sb) && |
| + (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT))) { |
| + if (block_group == 0) { |
| + ext4_unlock_group(sb, block_group); |
| + unlock_buffer(bh); |
| + ext4_error(sb, "Inode bitmap for bg 0 marked " |
| + "uninitialized"); |
| + err = -EFSCORRUPTED; |
| + goto out; |
| + } |
| memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); |
| ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), |
| sb->s_blocksize * 8, bh->b_data); |
| @@ -919,7 +928,8 @@ got: |
| |
| /* recheck and clear flag under lock if we still need to */ |
| ext4_lock_group(sb, group); |
| - if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { |
| + if (ext4_has_group_desc_csum(sb) && |
| + (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) { |
| gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); |
| ext4_free_group_clusters_set(sb, gdp, |
| ext4_free_clusters_after_init(sb, group, gdp)); |
| --- a/fs/ext4/mballoc.c |
| +++ b/fs/ext4/mballoc.c |
| @@ -2445,7 +2445,8 @@ int ext4_mb_add_groupinfo(struct super_b |
| * initialize bb_free to be able to skip |
| * empty groups without initialization |
| */ |
| - if (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { |
| + if (ext4_has_group_desc_csum(sb) && |
| + (desc->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) { |
| meta_group_info[i]->bb_free = |
| ext4_free_clusters_after_init(sb, group, desc); |
| } else { |
| @@ -2966,7 +2967,8 @@ ext4_mb_mark_diskspace_used(struct ext4_ |
| #endif |
| ext4_set_bits(bitmap_bh->b_data, ac->ac_b_ex.fe_start, |
| ac->ac_b_ex.fe_len); |
| - if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { |
| + if (ext4_has_group_desc_csum(sb) && |
| + (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))) { |
| gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); |
| ext4_free_group_clusters_set(sb, gdp, |
| ext4_free_clusters_after_init(sb, |
| --- a/fs/ext4/super.c |
| +++ b/fs/ext4/super.c |
| @@ -2867,13 +2867,22 @@ static ext4_group_t ext4_has_uninit_itab |
| ext4_group_t group, ngroups = EXT4_SB(sb)->s_groups_count; |
| struct ext4_group_desc *gdp = NULL; |
| |
| + if (!ext4_has_group_desc_csum(sb)) |
| + return ngroups; |
| + |
| for (group = 0; group < ngroups; group++) { |
| gdp = ext4_get_group_desc(sb, group, NULL); |
| if (!gdp) |
| continue; |
| |
| - if (!(gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED))) |
| + if (gdp->bg_flags & cpu_to_le16(EXT4_BG_INODE_ZEROED)) |
| + continue; |
| + if (group != 0) |
| break; |
| + ext4_error(sb, "Inode table for bg 0 marked as " |
| + "needing zeroing"); |
| + if (sb->s_flags & MS_RDONLY) |
| + return ngroups; |
| } |
| |
| return group; |