[PATCH v3] xfs: test reflux with exchange-range

"Darrick J. Wong" <[email protected]>
Newsgroups org.kernel.vger.linux-xfs,org.kernel.vger.fstests
Message-ID <20260822171525.GM839663@frogsfrogsfrogs>
From: Darrick J. Wong <[email protected]>

Regression test for the XFS_IOC_EXCHRANGE flag INO1_WRITTEN clearing
reflink flags when that shouldn't happen.

Signed-off-by: "Darrick J. Wong" <[email protected]>
Reviewed-by: Christoph Hellwig <[email protected]>
Reviewed-by: Zorro Lang <[email protected]
---
v3: remove dummydir, add more rvb, fix fixes tag
v2: add rvb tags, fix test description
---
 tests/generic/1957     |   65 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/generic/1957.out |   16 ++++++++++++
 2 files changed, 81 insertions(+)
 create mode 100755 tests/generic/1957
 create mode 100644 tests/generic/1957.out

diff --git a/tests/generic/1957 b/tests/generic/1957
new file mode 100755
index 00000000000000..693072a0866c2f
--- /dev/null
+++ b/tests/generic/1957
@@ -0,0 +1,65 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2026 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 1957
+#
+# Regression test for an exchange-range bug which unintentionally results in
+# files that share data blocks but don't have the reflink flag set.  This
+# enables attackers to rewrite shared blocks to gain root privileges, similar
+# to refluxfs.
+. ./common/preamble
+_begin_fstest auto quick fiexchange
+
+. ./common/filter
+. ./common/reflink
+
+_require_scratch_reflink
+_require_xfs_io_command exchangerange
+_require_cp_reflink
+
+_fixed_by_fs_commit xfs b2d5a81dae3853 \
+	"xfs: fix exchange-range reflink flag clearing issue with  INO1_WRITTEN"
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount
+
+# Create file1 as a fully written file, and file2 as a sparse file with one
+# written area.
+$XFS_IO_PROG -f -c "pwrite -S 0x58 0 1m" $SCRATCH_MNT/file1 >> $seqres.full
+$XFS_IO_PROG -f -c "truncate 1m" -c "pwrite -S 0x59 64k 64k" $SCRATCH_MNT/file2 >> $seqres.full
+
+_scratch_unmount
+_scratch_xfs_db -c "path /file1" -c 'print' -c "path /file2" -c 'print' | grep reflink
+_scratch_mount
+
+# Reflink file1 so that the reflink flag gets set
+_cp_reflink $SCRATCH_MNT/file1 $SCRATCH_MNT/fileC
+
+md5sum $SCRATCH_MNT/file1 $SCRATCH_MNT/file2 $SCRATCH_MNT/fileC | _filter_scratch
+
+# Exchange the written parts of file2 with file1, but only file1 has the
+# reflink flag set.
+$XFS_IO_PROG -c "exchangerange -w $SCRATCH_MNT/file2" $SCRATCH_MNT/file1 >> $seqres.full
+
+# Check reflink flags
+_scratch_unmount
+_scratch_xfs_db -c "path /file1" -c 'print' -c "path /file2" -c 'print' | grep reflink
+_scratch_mount
+
+# Record content and layout
+md5sum $SCRATCH_MNT/file1 $SCRATCH_MNT/file2 $SCRATCH_MNT/fileC | _filter_scratch
+$XFS_IO_PROG -c 'bmap -vvvvvvvv' $SCRATCH_MNT/file[12C] >> $seqres.full
+
+# Write a single byte to file1, does that get echoed in fileC?
+$XFS_IO_PROG -c 'pwrite -S 0x59 0 1' $SCRATCH_MNT/file1 >> $seqres.full
+#
+# Check reflink flags
+_scratch_unmount
+_scratch_xfs_db -c "path /file1" -c 'print' -c "path /file2" -c 'print' | grep reflink
+_scratch_mount
+
+md5sum $SCRATCH_MNT/file1 $SCRATCH_MNT/file2 $SCRATCH_MNT/fileC | _filter_scratch
+$XFS_IO_PROG -c 'bmap -vvvvvvvv' $SCRATCH_MNT/file[12C] >> $seqres.full
+
+_exit 0
diff --git a/tests/generic/1957.out b/tests/generic/1957.out
new file mode 100644
index 00000000000000..7d64ea0e2c372d
--- /dev/null
+++ b/tests/generic/1957.out
@@ -0,0 +1,16 @@
+QA output created by 1957
+v3.reflink = 0
+v3.reflink = 0
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/file1
+e6ee47dca6e786a44ae5b912be870d96  SCRATCH_MNT/file2
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/fileC
+v3.reflink = 1
+v3.reflink = 1
+b630c7de58afd0ba5b8dfa24d701b5b8  SCRATCH_MNT/file1
+18cc9868973139a0bb558e09802a51b6  SCRATCH_MNT/file2
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/fileC
+v3.reflink = 1
+v3.reflink = 1
+82b27c6f1b1de8fba44a911af80d9a7a  SCRATCH_MNT/file1
+18cc9868973139a0bb558e09802a51b6  SCRATCH_MNT/file2
+310f146ce52077fcd3308dcbe7632bb2  SCRATCH_MNT/fileC
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.