]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Mar 2021 17:09:30 +0000 (18:09 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Mar 2021 17:09:30 +0000 (18:09 +0100)
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

queue-4.14/series [new file with mode: 0644]
queue-4.4/ext4-check-journal-inode-extents-more-carefully.patch [new file with mode: 0644]
queue-4.4/ext4-don-t-allow-overlapping-system-zones.patch [new file with mode: 0644]
queue-4.4/ext4-handle-error-of-ext4_setup_system_zone-on-remount.patch [new file with mode: 0644]
queue-4.4/series [new file with mode: 0644]
queue-4.9/series [new file with mode: 0644]

diff --git a/queue-4.14/series b/queue-4.14/series
new file mode 100644 (file)
index 0000000..09a5175
--- /dev/null
@@ -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 (file)
index 0000000..e7106ad
--- /dev/null
@@ -0,0 +1,278 @@
+From foo@baz Wed Mar 17 06:09:14 PM CET 2021
+From: Jan Kara <jack@suse.cz>
+Date: Wed, 17 Mar 2021 17:44:14 +0100
+Subject: ext4: check journal inode extents more carefully
+To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: <stable@vger.kernel.org>, Jan Kara <jack@suse.cz>, Wolfgang Frisch <wolfgang.frisch@suse.com>, Lukas Czerner <lczerner@redhat.com>, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20210317164414.17364-4-jack@suse.cz>
+
+From: Jan Kara <jack@suse.cz>
+
+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 <wolfgang.frisch@suse.com>
+Reviewed-by: Lukas Czerner <lczerner@redhat.com>
+Signed-off-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20200728130437.7804-4-jack@suse.cz
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..a31ae44
--- /dev/null
@@ -0,0 +1,82 @@
+From foo@baz Wed Mar 17 06:09:14 PM CET 2021
+From: Jan Kara <jack@suse.cz>
+Date: Wed, 17 Mar 2021 17:44:13 +0100
+Subject: ext4: don't allow overlapping system zones
+To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: <stable@vger.kernel.org>, Jan Kara <jack@suse.cz>, Lukas Czerner <lczerner@redhat.com>, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20210317164414.17364-3-jack@suse.cz>
+
+From: Jan Kara <jack@suse.cz>
+
+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 <lczerner@redhat.com>
+Signed-off-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20200728130437.7804-3-jack@suse.cz
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..8243277
--- /dev/null
@@ -0,0 +1,37 @@
+From foo@baz Wed Mar 17 06:09:14 PM CET 2021
+From: Jan Kara <jack@suse.cz>
+Date: Wed, 17 Mar 2021 17:44:12 +0100
+Subject: ext4: handle error of ext4_setup_system_zone() on remount
+To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: <stable@vger.kernel.org>, Jan Kara <jack@suse.cz>, Lukas Czerner <lczerner@redhat.com>, Theodore Ts'o <tytso@mit.edu>
+Message-ID: <20210317164414.17364-2-jack@suse.cz>
+
+From: Jan Kara <jack@suse.cz>
+
+commit d176b1f62f242ab259ff665a26fbac69db1aecba upstream.
+
+ext4_setup_system_zone() can fail. Handle the failure in ext4_remount().
+
+Reviewed-by: Lukas Czerner <lczerner@redhat.com>
+Signed-off-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20200728130437.7804-2-jack@suse.cz
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..09a5175
--- /dev/null
@@ -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 (file)
index 0000000..09a5175
--- /dev/null
@@ -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