* [PATCH 0/2] btrfs-progs: fix a heap-overflow bug in raid56 reconstruction
@ 2026-05-22 10:06 Qu Wenruo
2026-05-22 10:06 ` [PATCH 1/2] btrfs-progs: fix raid56 reconstruction read Qu Wenruo
2026-05-22 10:06 ` [PATCH 2/2] btrfs-progs: fsck-tests: add a new test case for the raid56 read fix Qu Wenruo
0 siblings, 2 replies; 3+ messages in thread
From: Qu Wenruo @ 2026-05-22 10:06 UTC (permalink / raw)
To: linux-btrfs
During my local tests on a kernel patchset of falling back to
RAID1/RAID1C3 for single-data-RAID56, it turns out that "btrfs check
--check-data-csum" can report a lot of false csum mismatch.
It turns out that there is heap-overflow bug where we can get garbage
into the IO buffer for RAID56 reconstruction read.
Fix it and add a regression test case.
Qu Wenruo (2):
btrfs-progs: fix raid56 reconstruction read
btrfs-progs: fsck-tests: add a new test case for the raid56 read fix
Makefile | 2 +-
kernel-shared/extent_io.c | 14 +++++++++++-
tests/fsck-tests/074-raid56-read/test.sh | 27 ++++++++++++++++++++++++
3 files changed, 41 insertions(+), 2 deletions(-)
create mode 100755 tests/fsck-tests/074-raid56-read/test.sh
--
2.54.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] btrfs-progs: fix raid56 reconstruction read
2026-05-22 10:06 [PATCH 0/2] btrfs-progs: fix a heap-overflow bug in raid56 reconstruction Qu Wenruo
@ 2026-05-22 10:06 ` Qu Wenruo
2026-05-22 10:06 ` [PATCH 2/2] btrfs-progs: fsck-tests: add a new test case for the raid56 read fix Qu Wenruo
1 sibling, 0 replies; 3+ messages in thread
From: Qu Wenruo @ 2026-05-22 10:06 UTC (permalink / raw)
To: linux-btrfs
[BUG]
The following script can easily lead to false alerts from
"btrfs check --check-data-csum":
mkfs.btrfs -f -d raid5 $dev1 $dev2 > /dev/null
mount $dev1 $mnt
$fsstress -w -n 64 -d $mnt
umount $mnt
btrfs check --check-data-csum $dev1
Which will normally report a lot of csum mismatch on mirror 2:
Opening filesystem to check...
Checking filesystem on /dev/test/scratch1
UUID: e742b7b6-2562-48e5-b411-d50975633045
[1/8] checking log skipped (none written)
[2/8] checking root items
[3/8] checking extents
[4/8] checking free space tree
[5/8] checking fs roots
[6/8] checking csums against data
mirror 2 bytenr 298909696 csum 0xd800f135 expected csum 0x7f6b1842
mirror 2 bytenr 298913792 csum 0x1f49b645 expected csum 0x7f6b1842
...
mirror 2 bytenr 1372917760 csum 0x16f78e6b expected csum 0x9462d728
mirror 2 bytenr 1372921856 csum 0xa972667e expected csum 0x3bea937f
ERROR: errors found in csum tree
[7/8] checking root refs
[8/8] checking quota groups skipped (not enabled on this FS)
found 2736128 bytes used, error(s) found
And if asan is enabled, it will detect a heap-over-flow:
[6/8] checking csums against data
=================================================================
==6512==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7e982fa2c800 at pc 0x7f883111e4da bp 0x7ffc69e8c9a0 sp 0x7ffc69e8c148
READ of size 65536 at 0x7e982fa2c800 thread T0
#0 0x7f883111e4d9 in memcpy /usr/src/debug/gcc/gcc/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
#1 0x560b9f250c4a in read_raid56 kernel-shared/extent_io.c:413
#2 0x560b9f25100a in read_data_from_disk kernel-shared/extent_io.c:443
#3 0x560b9f3faa12 in check_extent_csums check/main.c:5964
#4 0x560b9f3fcaee in check_csum_root check/main.c:6235
#5 0x560b9f3fcef6 in check_csums check/main.c:6276
#6 0x560b9f41d168 in cmd_check check/main.c:11082
#7 0x560b9f1f04fb in cmd_execute cmds/commands.h:126
#8 0x560b9f1f1cc8 in main /home/adam/btrfs-progs/btrfs.c:474
#9 0x7f8830c27634 (/usr/lib/libc.so.6+0x27634) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d)
#10 0x7f8830c276e8 in __libc_start_main (/usr/lib/libc.so.6+0x276e8) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d)
#11 0x560b9f1f03d4 in _start (/home/adam/btrfs-progs/btrfs+0x883d4) (BuildId: 2fde8cac91b9fec1b45e1b5145ee594466b76396)
0x7e982fa2c800 is located 0 bytes after 65536-byte region [0x7e982fa1c800,0x7e982fa2c800)
allocated by thread T0 here:
#0 0x7f8831120cb5 in malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:67
#1 0x560b9f250874 in read_raid56 kernel-shared/extent_io.c:348
#2 0x560b9f25100a in read_data_from_disk kernel-shared/extent_io.c:443
#3 0x560b9f3faa12 in check_extent_csums check/main.c:5964
#4 0x560b9f3fcaee in check_csum_root check/main.c:6235
#5 0x560b9f3fcef6 in check_csums check/main.c:6276
#6 0x560b9f41d168 in cmd_check check/main.c:11082
#7 0x560b9f1f04fb in cmd_execute cmds/commands.h:126
#8 0x560b9f1f1cc8 in main /home/adam/btrfs-progs/btrfs.c:474
#9 0x7f8830c27634 (/usr/lib/libc.so.6+0x27634) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d)
#10 0x7f8830c276e8 in __libc_start_main (/usr/lib/libc.so.6+0x276e8) (BuildId: 2f722da304c0a508c891285e6840199c35019c8d)
#11 0x560b9f1f03d4 in _start (/home/adam/btrfs-progs/btrfs+0x883d4) (BuildId: 2fde8cac91b9fec1b45e1b5145ee594466b76396)
SUMMARY: AddressSanitizer: heap-buffer-overflow kernel-shared/extent_io.c:413 in read_raid56
Shadow bytes around the buggy address:
0x7e982fa2c580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7e982fa2c600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7e982fa2c680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7e982fa2c700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7e982fa2c780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7e982fa2c800:[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7e982fa2c880: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7e982fa2c900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7e982fa2c980: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7e982fa2ca00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7e982fa2ca80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
[CAUSE]
During read of mirrors other than mirror 0/1 from RAID56 profiles,
btrfs-progs go through the following call chain:
read_data_from_disk()
|- btrfs_map_block()
| Note that @read_len returned by btrfs_map_block() is always
| BTRFS_STRIPE_LEN.
|
\- read_raid56()
| Which does read out all stripes, but there is no extra checks
| against if the logical range crosses stripe boundaries.
|
|- raid56_recov()
| Which rebuilds the target stripe.
|
|- memcpy(buf, pointers[failed_a] + (logical - full_stripe_start) %
BTRFS_STRIPE_LEN, len);
If @logical is not at the begining of a stripe, then @logical + @len
will go beyond the stripe buffer size, causing gargage into the target
buffer.
[FIX]
Calculate the correct read length inside the data stripe for RAID56
inside read_data_from_disk().
And add extra ASSERT()s inside read_raid56().
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
kernel-shared/extent_io.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/kernel-shared/extent_io.c b/kernel-shared/extent_io.c
index cb48699c2e46..ea26d9e6c94b 100644
--- a/kernel-shared/extent_io.c
+++ b/kernel-shared/extent_io.c
@@ -324,6 +324,8 @@ static int read_raid56(struct btrfs_fs_info *fs_info, void *buf, u64 logical,
const int tolerance = (multi->type & BTRFS_RAID_RAID6 ? 2 : 1);
const int num_stripes = multi->num_stripes;
const u64 full_stripe_start = raid_map[0];
+ const u64 stripe_start = full_stripe_start +
+ round_down(logical - full_stripe_start, BTRFS_STRIPE_LEN);
void **pointers = NULL;
unsigned long *failed_stripe_bitmap = NULL;
int failed_a = -1;
@@ -336,7 +338,8 @@ static int read_raid56(struct btrfs_fs_info *fs_info, void *buf, u64 logical,
ASSERT(raid_map);
/* The read length should be inside one stripe */
- ASSERT(len <= BTRFS_STRIPE_LEN);
+ ASSERT(logical >= stripe_start &&
+ logical + len <= stripe_start + BTRFS_STRIPE_LEN);
pointers = calloc(num_stripes, sizeof(void *));
if (!pointers)
@@ -439,6 +442,15 @@ int read_data_from_disk(struct btrfs_fs_info *info, void *buf, u64 logical,
/* We need to rebuild from P/Q */
if (mirror > 1 && multi->type & BTRFS_BLOCK_GROUP_RAID56_MASK) {
+ u64 stripe_start;
+
+ /*
+ * The returned @read_len is always BTRFS_STRIPE_LEN. We have to calcualte
+ * the proper length to the stripe boundary.
+ */
+ stripe_start = raid_map[0] + round_down(logical - raid_map[0], BTRFS_STRIPE_LEN);
+ read_len = min((stripe_start + BTRFS_STRIPE_LEN) - logical, read_len);
+
ret = read_raid56(info, buf, logical, read_len, mirror, multi,
raid_map);
kfree(multi);
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [PATCH 2/2] btrfs-progs: fsck-tests: add a new test case for the raid56 read fix
2026-05-22 10:06 [PATCH 0/2] btrfs-progs: fix a heap-overflow bug in raid56 reconstruction Qu Wenruo
2026-05-22 10:06 ` [PATCH 1/2] btrfs-progs: fix raid56 reconstruction read Qu Wenruo
@ 2026-05-22 10:06 ` Qu Wenruo
1 sibling, 0 replies; 3+ messages in thread
From: Qu Wenruo @ 2026-05-22 10:06 UTC (permalink / raw)
To: linux-btrfs
The new test case is a regression test, which does:
- Create a btrfs with RAID5 data
- Fsstress it with 256 write operations
- Unmount
- Make sure "btrfs check --check-data-csum" reports no error
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
Makefile | 2 +-
tests/fsck-tests/074-raid56-read/test.sh | 27 ++++++++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
create mode 100755 tests/fsck-tests/074-raid56-read/test.sh
diff --git a/Makefile b/Makefile
index 4f471ff4a439..934d6dfb9c37 100644
--- a/Makefile
+++ b/Makefile
@@ -536,7 +536,7 @@ test-convert: btrfs btrfs-convert
test-check: test-fsck
test-check-lowmem: test-fsck
-test-fsck: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune
+test-fsck: btrfs btrfs-image btrfs-corrupt-block mkfs.btrfs btrfstune fsstress
ifneq ($(MAKECMDGOALS),test-check-lowmem)
@echo " TEST fsck-tests.sh"
$(Q)bash tests/fsck-tests.sh
diff --git a/tests/fsck-tests/074-raid56-read/test.sh b/tests/fsck-tests/074-raid56-read/test.sh
new file mode 100755
index 000000000000..6caae9837c35
--- /dev/null
+++ b/tests/fsck-tests/074-raid56-read/test.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Make sure "btrfs check" can properly handle RAID5 data reconstruction.
+
+source "$TEST_TOP/common" || exit
+
+check_prereq btrfs
+check_prereq mkfs.btrfs
+check_prereq fsstress
+check_global_prereq fallocate
+
+setup_root_helper
+setup_loopdevs 3
+prepare_loopdevs
+dev1=${loopdevs[1]}
+TEST_DEV=$dev1
+fsstress_prog="$INTERNAL_BIN/fsstress"
+
+run_check $SUDO_HELPER "$TOP/mkfs.btrfs" -f -m raid1 -d raid5 "${loopdevs[@]}"
+run_check_mount_test_dev
+run_check $SUDO_HELPER $fsstress_prog -w -n 256 -d "$TEST_MNT"
+run_check_umount_test_dev
+
+# Check data csum should not report false alerts
+run_check $SUDO_HELPER "$TOP/btrfs" check --check-data-csum "$dev1"
+
+cleanup_loopdevs
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-22 10:07 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-22 10:06 [PATCH 0/2] btrfs-progs: fix a heap-overflow bug in raid56 reconstruction Qu Wenruo
2026-05-22 10:06 ` [PATCH 1/2] btrfs-progs: fix raid56 reconstruction read Qu Wenruo
2026-05-22 10:06 ` [PATCH 2/2] btrfs-progs: fsck-tests: add a new test case for the raid56 read fix Qu Wenruo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox