]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 16 Jul 2024 14:25:01 +0000 (16:25 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 16 Jul 2024 14:25:01 +0000 (16:25 +0200)
added patches:
btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch

queue-6.6/btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch [new file with mode: 0644]
queue-6.6/series

diff --git a/queue-6.6/btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch b/queue-6.6/btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch
new file mode 100644 (file)
index 0000000..8977a76
--- /dev/null
@@ -0,0 +1,139 @@
+From 1645c283a87c61f84b2bffd81f50724df959b11a Mon Sep 17 00:00:00 2001
+From: Qu Wenruo <wqu@suse.com>
+Date: Tue, 24 Oct 2023 12:41:11 +1030
+Subject: btrfs: tree-checker: add type and sequence check for inline backrefs
+
+From: Qu Wenruo <wqu@suse.com>
+
+commit 1645c283a87c61f84b2bffd81f50724df959b11a upstream.
+
+[BUG]
+There is a bug report that ntfs2btrfs had a bug that it can lead to
+transaction abort and the filesystem flips to read-only.
+
+[CAUSE]
+For inline backref items, kernel has a strict requirement for their
+ordered, they must follow the following rules:
+
+- All btrfs_extent_inline_ref::type should be in an ascending order
+
+- Within the same type, the items should follow a descending order by
+  their sequence number
+
+  For EXTENT_DATA_REF type, the sequence number is result from
+  hash_extent_data_ref().
+  For other types, their sequence numbers are
+  btrfs_extent_inline_ref::offset.
+
+Thus if there is any code not following above rules, the resulted
+inline backrefs can prevent the kernel to locate the needed inline
+backref and lead to transaction abort.
+
+[FIX]
+Ntrfs2btrfs has already fixed the problem, and btrfs-progs has added the
+ability to detect such problems.
+
+For kernel, let's be more noisy and be more specific about the order, so
+that the next time kernel hits such problem we would reject it in the
+first place, without leading to transaction abort.
+
+Link: https://github.com/kdave/btrfs-progs/pull/622
+Reviewed-by: Josef Bacik <josef@toxicpanda.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+[ Fix a conflict due to header cleanup. ]
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/btrfs/tree-checker.c |   39 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 39 insertions(+)
+
+--- a/fs/btrfs/tree-checker.c
++++ b/fs/btrfs/tree-checker.c
+@@ -29,6 +29,7 @@
+ #include "accessors.h"
+ #include "file-item.h"
+ #include "inode-item.h"
++#include "extent-tree.h"
+ /*
+  * Error message should follow the following format:
+@@ -1274,6 +1275,8 @@ static int check_extent_item(struct exte
+       unsigned long ptr;      /* Current pointer inside inline refs */
+       unsigned long end;      /* Extent item end */
+       const u32 item_size = btrfs_item_size(leaf, slot);
++      u8 last_type = 0;
++      u64 last_seq = U64_MAX;
+       u64 flags;
+       u64 generation;
+       u64 total_refs;         /* Total refs in btrfs_extent_item */
+@@ -1320,6 +1323,18 @@ static int check_extent_item(struct exte
+        *    2.2) Ref type specific data
+        *         Either using btrfs_extent_inline_ref::offset, or specific
+        *         data structure.
++       *
++       *    All above inline items should follow the order:
++       *
++       *    - All btrfs_extent_inline_ref::type should be in an ascending
++       *      order
++       *
++       *    - Within the same type, the items should follow a descending
++       *      order by their sequence number. The sequence number is
++       *      determined by:
++       *      * btrfs_extent_inline_ref::offset for all types  other than
++       *        EXTENT_DATA_REF
++       *      * hash_extent_data_ref() for EXTENT_DATA_REF
+        */
+       if (unlikely(item_size < sizeof(*ei))) {
+               extent_err(leaf, slot,
+@@ -1401,6 +1416,7 @@ static int check_extent_item(struct exte
+               struct btrfs_extent_inline_ref *iref;
+               struct btrfs_extent_data_ref *dref;
+               struct btrfs_shared_data_ref *sref;
++              u64 seq;
+               u64 dref_offset;
+               u64 inline_offset;
+               u8 inline_type;
+@@ -1414,6 +1430,7 @@ static int check_extent_item(struct exte
+               iref = (struct btrfs_extent_inline_ref *)ptr;
+               inline_type = btrfs_extent_inline_ref_type(leaf, iref);
+               inline_offset = btrfs_extent_inline_ref_offset(leaf, iref);
++              seq = inline_offset;
+               if (unlikely(ptr + btrfs_extent_inline_ref_size(inline_type) > end)) {
+                       extent_err(leaf, slot,
+ "inline ref item overflows extent item, ptr %lu iref size %u end %lu",
+@@ -1444,6 +1461,10 @@ static int check_extent_item(struct exte
+               case BTRFS_EXTENT_DATA_REF_KEY:
+                       dref = (struct btrfs_extent_data_ref *)(&iref->offset);
+                       dref_offset = btrfs_extent_data_ref_offset(leaf, dref);
++                      seq = hash_extent_data_ref(
++                                      btrfs_extent_data_ref_root(leaf, dref),
++                                      btrfs_extent_data_ref_objectid(leaf, dref),
++                                      btrfs_extent_data_ref_offset(leaf, dref));
+                       if (unlikely(!IS_ALIGNED(dref_offset,
+                                                fs_info->sectorsize))) {
+                               extent_err(leaf, slot,
+@@ -1470,6 +1491,24 @@ static int check_extent_item(struct exte
+                                  inline_type);
+                       return -EUCLEAN;
+               }
++              if (inline_type < last_type) {
++                      extent_err(leaf, slot,
++                                 "inline ref out-of-order: has type %u, prev type %u",
++                                 inline_type, last_type);
++                      return -EUCLEAN;
++              }
++              /* Type changed, allow the sequence starts from U64_MAX again. */
++              if (inline_type > last_type)
++                      last_seq = U64_MAX;
++              if (seq > last_seq) {
++                      extent_err(leaf, slot,
++"inline ref out-of-order: has type %u offset %llu seq 0x%llx, prev type %u seq 0x%llx",
++                                 inline_type, inline_offset, seq,
++                                 last_type, last_seq);
++                      return -EUCLEAN;
++              }
++              last_type = inline_type;
++              last_seq = seq;
+               ptr += btrfs_extent_inline_ref_size(inline_type);
+       }
+       /* No padding is allowed */
index bbf186e4a32f480823f3446b7e2a937eecf624e1..9a587b62e17f626578827e9be6218b6092cce2b8 100644 (file)
@@ -106,3 +106,4 @@ selftests-net-fix-gro.c-compilation-failure-due-to-non-existent-opt_ipproto_off.
 nilfs2-fix-kernel-bug-on-rename-operation-of-broken-directory.patch
 ext4-avoid-ptr-null-pointer-dereference.patch
 sched-move-psi_account_irqtime-out-of-update_rq_clock_task-hotpath.patch
+btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch