From: Greg Kroah-Hartman Date: Wed, 17 Mar 2021 17:09:30 +0000 (+0100) Subject: 4.4-stable patches X-Git-Tag: v4.19.182~32 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6e5cf9b0681794e75e4f1ac314824629fe072f1f;p=thirdparty%2Fkernel%2Fstable-queue.git 4.4-stable patches added patches: ext4-check-journal-inode-extents-more-carefully.patch ext4-don-t-allow-overlapping-system-zones.patch ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch --- diff --git a/queue-4.14/series b/queue-4.14/series new file mode 100644 index 00000000000..09a51753b89 --- /dev/null +++ b/queue-4.14/series @@ -0,0 +1,3 @@ +ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch +ext4-don-t-allow-overlapping-system-zones.patch +ext4-check-journal-inode-extents-more-carefully.patch diff --git a/queue-4.4/ext4-check-journal-inode-extents-more-carefully.patch b/queue-4.4/ext4-check-journal-inode-extents-more-carefully.patch new file mode 100644 index 00000000000..e7106ad6778 --- /dev/null +++ b/queue-4.4/ext4-check-journal-inode-extents-more-carefully.patch @@ -0,0 +1,278 @@ +From foo@baz Wed Mar 17 06:09:14 PM CET 2021 +From: Jan Kara +Date: Wed, 17 Mar 2021 17:44:14 +0100 +Subject: ext4: check journal inode extents more carefully +To: Greg Kroah-Hartman +Cc: , Jan Kara , Wolfgang Frisch , Lukas Czerner , Theodore Ts'o +Message-ID: <20210317164414.17364-4-jack@suse.cz> + +From: Jan Kara + +commit ce9f24cccdc019229b70a5c15e2b09ad9c0ab5d1 upstream. + +Currently, system zones just track ranges of block, that are "important" +fs metadata (bitmaps, group descriptors, journal blocks, etc.). This +however complicates how extent tree (or indirect blocks) can be checked +for inodes that actually track such metadata - currently the journal +inode but arguably we should be treating quota files or resize inode +similarly. We cannot run __ext4_ext_check() on such metadata inodes when +loading their extents as that would immediately trigger the validity +checks and so we just hack around that and special-case the journal +inode. This however leads to a situation that a journal inode which has +extent tree of depth at least one can have invalid extent tree that gets +unnoticed until ext4_cache_extents() crashes. + +To overcome this limitation, track inode number each system zone belongs +to (0 is used for zones not belonging to any inode). We can then verify +inode number matches the expected one when verifying extent tree and +thus avoid the false errors. With this there's no need to to +special-case journal inode during extent tree checking anymore so remove +it. + +Fixes: 0a944e8a6c66 ("ext4: don't perform block validity checks on the journal inode") +Reported-by: Wolfgang Frisch +Reviewed-by: Lukas Czerner +Signed-off-by: Jan Kara +Link: https://lore.kernel.org/r/20200728130437.7804-4-jack@suse.cz +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/block_validity.c | 37 +++++++++++++++++++++---------------- + fs/ext4/ext4.h | 6 +++--- + fs/ext4/extents.c | 16 ++++++---------- + fs/ext4/indirect.c | 6 ++---- + fs/ext4/inode.c | 5 ++--- + fs/ext4/mballoc.c | 4 ++-- + 6 files changed, 36 insertions(+), 38 deletions(-) + +--- a/fs/ext4/block_validity.c ++++ b/fs/ext4/block_validity.c +@@ -23,6 +23,7 @@ struct ext4_system_zone { + struct rb_node node; + ext4_fsblk_t start_blk; + unsigned int count; ++ u32 ino; + }; + + static struct kmem_cache *ext4_system_zone_cachep; +@@ -43,7 +44,8 @@ void ext4_exit_system_zone(void) + static inline int can_merge(struct ext4_system_zone *entry1, + struct ext4_system_zone *entry2) + { +- if ((entry1->start_blk + entry1->count) == entry2->start_blk) ++ if ((entry1->start_blk + entry1->count) == entry2->start_blk && ++ entry1->ino == entry2->ino) + return 1; + return 0; + } +@@ -55,7 +57,7 @@ static inline int can_merge(struct ext4_ + */ + static int add_system_zone(struct ext4_sb_info *sbi, + ext4_fsblk_t start_blk, +- unsigned int count) ++ unsigned int count, u32 ino) + { + struct ext4_system_zone *new_entry, *entry; + struct rb_node **n = &sbi->system_blks.rb_node, *node; +@@ -78,6 +80,7 @@ static int add_system_zone(struct ext4_s + return -ENOMEM; + new_entry->start_blk = start_blk; + new_entry->count = count; ++ new_entry->ino = ino; + new_node = &new_entry->node; + + rb_link_node(new_node, parent, n); +@@ -153,16 +156,16 @@ static int ext4_protect_reserved_inode(s + if (n == 0) { + i++; + } else { +- if (!ext4_data_block_valid(sbi, map.m_pblk, n)) { +- ext4_error(sb, "blocks %llu-%llu from inode %u " ++ err = add_system_zone(sbi, map.m_pblk, n, ino); ++ if (err < 0) { ++ if (err == -EFSCORRUPTED) { ++ ext4_error(sb, ++ "blocks %llu-%llu from inode %u " + "overlap system zone", map.m_pblk, + map.m_pblk + map.m_len - 1, ino); +- err = -EFSCORRUPTED; ++ } + break; + } +- err = add_system_zone(sbi, map.m_pblk, n); +- if (err < 0) +- break; + i += n; + } + } +@@ -191,16 +194,16 @@ int ext4_setup_system_zone(struct super_ + if (ext4_bg_has_super(sb, i) && + ((i < 5) || ((i % flex_size) == 0))) + add_system_zone(sbi, ext4_group_first_block_no(sb, i), +- ext4_bg_num_gdb(sb, i) + 1); ++ ext4_bg_num_gdb(sb, i) + 1, 0); + gdp = ext4_get_group_desc(sb, i, NULL); +- ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1); ++ ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1, 0); + if (ret) + return ret; +- ret = add_system_zone(sbi, ext4_inode_bitmap(sb, gdp), 1); ++ ret = add_system_zone(sbi, ext4_inode_bitmap(sb, gdp), 1, 0); + if (ret) + return ret; + ret = add_system_zone(sbi, ext4_inode_table(sb, gdp), +- sbi->s_itb_per_group); ++ sbi->s_itb_per_group, 0); + if (ret) + return ret; + } +@@ -233,10 +236,11 @@ void ext4_release_system_zone(struct sup + * start_blk+count) is valid; 0 if some part of the block region + * overlaps with filesystem metadata blocks. + */ +-int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk, +- unsigned int count) ++int ext4_inode_block_valid(struct inode *inode, ext4_fsblk_t start_blk, ++ unsigned int count) + { + struct ext4_system_zone *entry; ++ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + struct rb_node *n = sbi->system_blks.rb_node; + + if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || +@@ -252,6 +256,8 @@ int ext4_data_block_valid(struct ext4_sb + else if (start_blk >= (entry->start_blk + entry->count)) + n = n->rb_right; + else { ++ if (entry->ino == inode->i_ino) ++ return 1; + sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); + return 0; + } +@@ -274,8 +280,7 @@ int ext4_check_blockref(const char *func + while (bref < p+max) { + blk = le32_to_cpu(*bref++); + if (blk && +- unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb), +- blk, 1))) { ++ unlikely(!ext4_inode_block_valid(inode, blk, 1))) { + es->s_last_error_block = cpu_to_le64(blk); + ext4_error_inode(inode, function, line, blk, + "invalid block"); +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -3134,9 +3134,9 @@ extern void ext4_release_system_zone(str + extern int ext4_setup_system_zone(struct super_block *sb); + extern int __init ext4_init_system_zone(void); + extern void ext4_exit_system_zone(void); +-extern int ext4_data_block_valid(struct ext4_sb_info *sbi, +- ext4_fsblk_t start_blk, +- unsigned int count); ++extern int ext4_inode_block_valid(struct inode *inode, ++ ext4_fsblk_t start_blk, ++ unsigned int count); + extern int ext4_check_blockref(const char *, unsigned int, + struct inode *, __le32 *, unsigned int); + +--- a/fs/ext4/extents.c ++++ b/fs/ext4/extents.c +@@ -384,7 +384,7 @@ static int ext4_valid_extent(struct inod + */ + if (lblock + len <= lblock) + return 0; +- return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, len); ++ return ext4_inode_block_valid(inode, block, len); + } + + static int ext4_valid_extent_idx(struct inode *inode, +@@ -392,7 +392,7 @@ static int ext4_valid_extent_idx(struct + { + ext4_fsblk_t block = ext4_idx_pblock(ext_idx); + +- return ext4_data_block_valid(EXT4_SB(inode->i_sb), block, 1); ++ return ext4_inode_block_valid(inode, block, 1); + } + + static int ext4_valid_extent_entries(struct inode *inode, +@@ -549,14 +549,10 @@ __read_extent_tree_block(const char *fun + } + if (buffer_verified(bh) && !(flags & EXT4_EX_FORCE_CACHE)) + return bh; +- if (!ext4_has_feature_journal(inode->i_sb) || +- (inode->i_ino != +- le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) { +- err = __ext4_ext_check(function, line, inode, +- ext_block_hdr(bh), depth, pblk); +- if (err) +- goto errout; +- } ++ err = __ext4_ext_check(function, line, inode, ++ ext_block_hdr(bh), depth, pblk); ++ if (err) ++ goto errout; + set_buffer_verified(bh); + /* + * If this is a leaf block, cache all of its entries +--- a/fs/ext4/indirect.c ++++ b/fs/ext4/indirect.c +@@ -946,8 +946,7 @@ static int ext4_clear_blocks(handle_t *h + else if (ext4_should_journal_data(inode)) + flags |= EXT4_FREE_BLOCKS_FORGET; + +- if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free, +- count)) { ++ if (!ext4_inode_block_valid(inode, block_to_free, count)) { + EXT4_ERROR_INODE(inode, "attempt to clear invalid " + "blocks %llu len %lu", + (unsigned long long) block_to_free, count); +@@ -1109,8 +1108,7 @@ static void ext4_free_branches(handle_t + if (!nr) + continue; /* A hole */ + +- if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), +- nr, 1)) { ++ if (!ext4_inode_block_valid(inode, nr, 1)) { + EXT4_ERROR_INODE(inode, + "invalid indirect mapped " + "block %lu (level %d)", +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -381,8 +381,7 @@ static int __check_block_validity(struct + (inode->i_ino == + le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_journal_inum))) + return 0; +- if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk, +- map->m_len)) { ++ if (!ext4_inode_block_valid(inode, map->m_pblk, map->m_len)) { + ext4_error_inode(inode, func, line, map->m_pblk, + "lblock %lu mapped to illegal pblock %llu " + "(length %d)", (unsigned long) map->m_lblk, +@@ -4437,7 +4436,7 @@ struct inode *__ext4_iget(struct super_b + + ret = 0; + if (ei->i_file_acl && +- !ext4_data_block_valid(EXT4_SB(sb), ei->i_file_acl, 1)) { ++ !ext4_inode_block_valid(inode, ei->i_file_acl, 1)) { + ext4_error_inode(inode, function, line, 0, + "iget: bad extended attribute block %llu", + ei->i_file_acl); +--- a/fs/ext4/mballoc.c ++++ b/fs/ext4/mballoc.c +@@ -2960,7 +2960,7 @@ ext4_mb_mark_diskspace_used(struct ext4_ + block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex); + + len = EXT4_C2B(sbi, ac->ac_b_ex.fe_len); +- if (!ext4_data_block_valid(sbi, block, len)) { ++ if (!ext4_inode_block_valid(ac->ac_inode, block, len)) { + ext4_error(sb, "Allocating blocks %llu-%llu which overlap " + "fs metadata", block, block+len); + /* File system mounted not to panic on error +@@ -4718,7 +4718,7 @@ void ext4_free_blocks(handle_t *handle, + + sbi = EXT4_SB(sb); + if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) && +- !ext4_data_block_valid(sbi, block, count)) { ++ !ext4_inode_block_valid(inode, block, count)) { + ext4_error(sb, "Freeing blocks not in datazone - " + "block = %llu, count = %lu", block, count); + goto error_return; diff --git a/queue-4.4/ext4-don-t-allow-overlapping-system-zones.patch b/queue-4.4/ext4-don-t-allow-overlapping-system-zones.patch new file mode 100644 index 00000000000..a31ae44f4c3 --- /dev/null +++ b/queue-4.4/ext4-don-t-allow-overlapping-system-zones.patch @@ -0,0 +1,82 @@ +From foo@baz Wed Mar 17 06:09:14 PM CET 2021 +From: Jan Kara +Date: Wed, 17 Mar 2021 17:44:13 +0100 +Subject: ext4: don't allow overlapping system zones +To: Greg Kroah-Hartman +Cc: , Jan Kara , Lukas Czerner , Theodore Ts'o +Message-ID: <20210317164414.17364-3-jack@suse.cz> + +From: Jan Kara + +commit bf9a379d0980e7413d94cb18dac73db2bfc5f470 upstream. + +Currently, add_system_zone() just silently merges two added system zones +that overlap. However the overlap should not happen and it generally +suggests that some unrelated metadata overlap which indicates the fs is +corrupted. We should have caught such problems earlier (e.g. in +ext4_check_descriptors()) but add this check as another line of defense. +In later patch we also use this for stricter checking of journal inode +extent tree. + +Reviewed-by: Lukas Czerner +Signed-off-by: Jan Kara +Link: https://lore.kernel.org/r/20200728130437.7804-3-jack@suse.cz +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/block_validity.c | 34 ++++++++++++---------------------- + 1 file changed, 12 insertions(+), 22 deletions(-) + +--- a/fs/ext4/block_validity.c ++++ b/fs/ext4/block_validity.c +@@ -57,7 +57,7 @@ static int add_system_zone(struct ext4_s + ext4_fsblk_t start_blk, + unsigned int count) + { +- struct ext4_system_zone *new_entry = NULL, *entry; ++ struct ext4_system_zone *new_entry, *entry; + struct rb_node **n = &sbi->system_blks.rb_node, *node; + struct rb_node *parent = NULL, *new_node = NULL; + +@@ -68,30 +68,20 @@ static int add_system_zone(struct ext4_s + n = &(*n)->rb_left; + else if (start_blk >= (entry->start_blk + entry->count)) + n = &(*n)->rb_right; +- else { +- if (start_blk + count > (entry->start_blk + +- entry->count)) +- entry->count = (start_blk + count - +- entry->start_blk); +- new_node = *n; +- new_entry = rb_entry(new_node, struct ext4_system_zone, +- node); +- break; +- } ++ else /* Unexpected overlap of system zones. */ ++ return -EFSCORRUPTED; + } + +- if (!new_entry) { +- new_entry = kmem_cache_alloc(ext4_system_zone_cachep, +- GFP_KERNEL); +- if (!new_entry) +- return -ENOMEM; +- new_entry->start_blk = start_blk; +- new_entry->count = count; +- new_node = &new_entry->node; ++ new_entry = kmem_cache_alloc(ext4_system_zone_cachep, ++ GFP_KERNEL); ++ if (!new_entry) ++ return -ENOMEM; ++ new_entry->start_blk = start_blk; ++ new_entry->count = count; ++ new_node = &new_entry->node; + +- rb_link_node(new_node, parent, n); +- rb_insert_color(new_node, &sbi->system_blks); +- } ++ rb_link_node(new_node, parent, n); ++ rb_insert_color(new_node, &sbi->system_blks); + + /* Can we merge to the left? */ + node = rb_prev(new_node); diff --git a/queue-4.4/ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch b/queue-4.4/ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch new file mode 100644 index 00000000000..82432774760 --- /dev/null +++ b/queue-4.4/ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch @@ -0,0 +1,37 @@ +From foo@baz Wed Mar 17 06:09:14 PM CET 2021 +From: Jan Kara +Date: Wed, 17 Mar 2021 17:44:12 +0100 +Subject: ext4: handle error of ext4_setup_system_zone() on remount +To: Greg Kroah-Hartman +Cc: , Jan Kara , Lukas Czerner , Theodore Ts'o +Message-ID: <20210317164414.17364-2-jack@suse.cz> + +From: Jan Kara + +commit d176b1f62f242ab259ff665a26fbac69db1aecba upstream. + +ext4_setup_system_zone() can fail. Handle the failure in ext4_remount(). + +Reviewed-by: Lukas Czerner +Signed-off-by: Jan Kara +Link: https://lore.kernel.org/r/20200728130437.7804-2-jack@suse.cz +Signed-off-by: Theodore Ts'o +Signed-off-by: Greg Kroah-Hartman +--- + fs/ext4/super.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -4968,7 +4968,10 @@ static int ext4_remount(struct super_blo + ext4_register_li_request(sb, first_not_zeroed); + } + +- ext4_setup_system_zone(sb); ++ err = ext4_setup_system_zone(sb); ++ if (err) ++ goto restore_opts; ++ + if (sbi->s_journal == NULL && !(old_sb_flags & MS_RDONLY)) + ext4_commit_super(sb, 1); + diff --git a/queue-4.4/series b/queue-4.4/series new file mode 100644 index 00000000000..09a51753b89 --- /dev/null +++ b/queue-4.4/series @@ -0,0 +1,3 @@ +ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch +ext4-don-t-allow-overlapping-system-zones.patch +ext4-check-journal-inode-extents-more-carefully.patch diff --git a/queue-4.9/series b/queue-4.9/series new file mode 100644 index 00000000000..09a51753b89 --- /dev/null +++ b/queue-4.9/series @@ -0,0 +1,3 @@ +ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch +ext4-don-t-allow-overlapping-system-zones.patch +ext4-check-journal-inode-extents-more-carefully.patch