From: Greg Kroah-Hartman Date: Tue, 16 Jul 2024 14:25:01 +0000 (+0200) Subject: 6.6-stable patches X-Git-Tag: v4.19.318~19 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1ca987eb37e6f2a9f06ae40c50f9499682ea221d;p=thirdparty%2Fkernel%2Fstable-queue.git 6.6-stable patches added patches: btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch --- 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 index 00000000000..8977a761790 --- /dev/null +++ b/queue-6.6/btrfs-tree-checker-add-type-and-sequence-check-for-inline-backrefs.patch @@ -0,0 +1,139 @@ +From 1645c283a87c61f84b2bffd81f50724df959b11a Mon Sep 17 00:00:00 2001 +From: Qu Wenruo +Date: Tue, 24 Oct 2023 12:41:11 +1030 +Subject: btrfs: tree-checker: add type and sequence check for inline backrefs + +From: Qu Wenruo + +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 +Signed-off-by: David Sterba +[ Fix a conflict due to header cleanup. ] +Signed-off-by: Qu Wenruo +Signed-off-by: Greg Kroah-Hartman +--- + 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 */ diff --git a/queue-6.6/series b/queue-6.6/series index bbf186e4a32..9a587b62e17 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -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