From: Sasha Levin <sashal@kernel.org>
To: patches@lists.linux.dev, stable@vger.kernel.org
Cc: Trond Myklebust <trond.myklebust@hammerspace.com>,
Olga Kornievskaia <okorniev@redhat.com>,
Sasha Levin <sashal@kernel.org>,
trondmy@kernel.org, anna@kernel.org, linux-nfs@vger.kernel.org
Subject: [PATCH AUTOSEL 6.16] NFSv4.2: Protect copy offload and clone against 'eof page pollution'
Date: Tue, 16 Sep 2025 09:58:55 -0400 [thread overview]
Message-ID: <20250916135936.1450850-2-sashal@kernel.org> (raw)
In-Reply-To: <20250916135936.1450850-1-sashal@kernel.org>
From: Trond Myklebust <trond.myklebust@hammerspace.com>
[ Upstream commit b2036bb65114c01caf4a1afe553026e081703c8c ]
The NFSv4.2 copy offload and clone functions can also end up extending
the size of the destination file, so they too need to call
nfs_truncate_last_folio().
Reported-by: Olga Kornievskaia <okorniev@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Based on my exhaustive investigation, here is my determination:
## **Backport Status: YES**
This commit should be backported to stable kernel trees.
## Detailed Justification:
### 1. **Fixes a Critical Data Integrity Bug**
The commit addresses "EOF page pollution" - a serious data corruption
issue where garbage data can become visible in files after copy/clone
operations. The code shows that when NFSv4.2 copy offload or clone
operations extend a file, mmap'd data written beyond the old EOF can
incorrectly become part of the file, violating POSIX semantics and user
expectations.
### 2. **Completes a Previous Incomplete Fix**
This is a follow-up to commit `b1817b18ff20e` which fixed the same issue
for other NFS operations but missed the copy/clone code paths. The
original fix addressed nfs_write_begin(), nfs_setattr(),
nfs42_proc_allocate(), and nfs42_proc_zero_range(), but overlooked the
copy and clone operations. This commit completes that fix.
### 3. **Small and Contained Change**
The change is minimal (13 insertions, 6 deletions) and surgical:
- Adds one `loff_t oldsize` variable capture in two functions
- Modifies `nfs42_copy_dest_done()` signature to accept the old size
- Adds a single `nfs_truncate_last_folio()` call in the common path
- Changes are confined entirely to fs/nfs/nfs42proc.c
### 4. **Clear Bug Fix, Not a Feature**
The commit purely fixes incorrect behavior - it doesn't add new
functionality or change APIs. It ensures that newly extended file
regions contain zeros as expected, rather than garbage data from
previous mmap writes.
### 5. **Low Risk of Regression**
- The `nfs_truncate_last_folio()` function includes safety checks (early
return if from >= to, handles lock failures gracefully)
- Only operates on a single folio at the EOF boundary
- The same fix pattern is already proven in other NFS operations since
September 2025
- No changes to external interfaces or APIs
### 6. **Affects User-Visible Behavior**
Users could experience data corruption when using NFSv4.2
copy_file_range() or clone operations that extend files. This is
particularly problematic for:
- Database applications using copy offload
- Backup/restore operations
- Container storage systems using clone operations
- Any application using mmap() in combination with copy/clone
### 7. **Testing Coverage**
The issue is detected by xfstest generic/363, providing a clear test
case for validation. The commit message indicates this was reported by
Olga Kornievskaia from Red Hat, suggesting it was found in production or
testing environments.
### 8. **Follows Stable Rules**
According to stable kernel rules, this qualifies because it:
- Fixes a bug that bothers users (data corruption)
- Is already in Linus' tree
- Is small (under 100 lines)
- Fixes only one thing
- Has no dependencies on other patches
### 9. **Similar Issues Were Backported**
The investigation shows similar NFS copy/clone fixes were marked for
stable:
- `3f015d89a47cd` "NFSv42: Fix pagecache invalidation after COPY/CLONE"
(Cc: stable)
- This establishes precedent for backporting NFSv4.2 copy/clone data
integrity fixes
### 10. **Cross-Filesystem Issue**
The same "EOF page pollution" issue affected multiple filesystems (ext4,
btrfs, f2fs), indicating this is a fundamental correctness issue that
should be fixed in all supported kernels.
The commit represents a critical data integrity fix that should be
backported to prevent data corruption in NFSv4.2 copy and clone
operations on stable kernels.
fs/nfs/nfs42proc.c | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/fs/nfs/nfs42proc.c b/fs/nfs/nfs42proc.c
index 4420b8740e2ff..e2fea37c53484 100644
--- a/fs/nfs/nfs42proc.c
+++ b/fs/nfs/nfs42proc.c
@@ -362,22 +362,27 @@ static int process_copy_commit(struct file *dst, loff_t pos_dst,
/**
* nfs42_copy_dest_done - perform inode cache updates after clone/copy offload
- * @inode: pointer to destination inode
+ * @file: pointer to destination file
* @pos: destination offset
* @len: copy length
+ * @oldsize: length of the file prior to clone/copy
*
* Punch a hole in the inode page cache, so that the NFS client will
* know to retrieve new data.
* Update the file size if necessary, and then mark the inode as having
* invalid cached values for change attribute, ctime, mtime and space used.
*/
-static void nfs42_copy_dest_done(struct inode *inode, loff_t pos, loff_t len)
+static void nfs42_copy_dest_done(struct file *file, loff_t pos, loff_t len,
+ loff_t oldsize)
{
+ struct inode *inode = file_inode(file);
+ struct address_space *mapping = file->f_mapping;
loff_t newsize = pos + len;
loff_t end = newsize - 1;
- WARN_ON_ONCE(invalidate_inode_pages2_range(inode->i_mapping,
- pos >> PAGE_SHIFT, end >> PAGE_SHIFT));
+ nfs_truncate_last_folio(mapping, oldsize, pos);
+ WARN_ON_ONCE(invalidate_inode_pages2_range(mapping, pos >> PAGE_SHIFT,
+ end >> PAGE_SHIFT));
spin_lock(&inode->i_lock);
if (newsize > i_size_read(inode))
@@ -410,6 +415,7 @@ static ssize_t _nfs42_proc_copy(struct file *src,
struct nfs_server *src_server = NFS_SERVER(src_inode);
loff_t pos_src = args->src_pos;
loff_t pos_dst = args->dst_pos;
+ loff_t oldsize_dst = i_size_read(dst_inode);
size_t count = args->count;
ssize_t status;
@@ -483,7 +489,7 @@ static ssize_t _nfs42_proc_copy(struct file *src,
goto out;
}
- nfs42_copy_dest_done(dst_inode, pos_dst, res->write_res.count);
+ nfs42_copy_dest_done(dst, pos_dst, res->write_res.count, oldsize_dst);
nfs_invalidate_atime(src_inode);
status = res->write_res.count;
out:
@@ -1250,6 +1256,7 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
struct nfs42_clone_res res = {
.server = server,
};
+ loff_t oldsize_dst = i_size_read(dst_inode);
int status;
msg->rpc_argp = &args;
@@ -1284,7 +1291,7 @@ static int _nfs42_proc_clone(struct rpc_message *msg, struct file *src_f,
/* a zero-length count means clone to EOF in src */
if (count == 0 && res.dst_fattr->valid & NFS_ATTR_FATTR_SIZE)
count = nfs_size_to_loff_t(res.dst_fattr->size) - dst_offset;
- nfs42_copy_dest_done(dst_inode, dst_offset, count);
+ nfs42_copy_dest_done(dst_f, dst_offset, count, oldsize_dst);
status = nfs_post_op_update_inode(dst_inode, res.dst_fattr);
}
--
2.51.0
next prev parent reply other threads:[~2025-09-16 13:59 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-16 13:58 [PATCH AUTOSEL 6.16] drm/amdkfd: fix p2p links bug in topology Sasha Levin
2025-09-16 13:58 ` Sasha Levin [this message]
2025-09-16 13:58 ` [PATCH AUTOSEL 6.16-6.12] bpf: Check the helper function is valid in get_helper_proto Sasha Levin
2025-09-16 13:58 ` [PATCH AUTOSEL 6.16-5.4] can: rcar_can: rcar_can_resume(): fix s2ram with PSCI Sasha Levin
2025-09-16 13:58 ` [PATCH AUTOSEL 6.16] NFS: Protect against 'eof page pollution' Sasha Levin
2025-09-16 13:58 ` [PATCH AUTOSEL 6.16] amd/amdkfd: correct mem limit calculation for small APUs Sasha Levin
2025-09-16 13:59 ` [PATCH AUTOSEL 6.16-6.12] btrfs: don't allow adding block device of less than 1 MB Sasha Levin
2025-09-16 18:58 ` Mark Harmstone
2025-09-16 13:59 ` [PATCH AUTOSEL 6.16] selftests/fs/mount-notify: Fix compilation failure Sasha Levin
2025-09-16 13:59 ` [PATCH AUTOSEL 6.16] selftests/bpf: Skip timer cases when bpf_timer is not supported Sasha Levin
2025-09-16 13:59 ` [PATCH AUTOSEL 6.16-5.15] bpf: Reject bpf_timer for PREEMPT_RT Sasha Levin
2025-09-16 13:59 ` [PATCH AUTOSEL 6.16-6.6] wifi: virt_wifi: Fix page fault on connect Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250916135936.1450850-2-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=anna@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=okorniev@redhat.com \
--cc=patches@lists.linux.dev \
--cc=stable@vger.kernel.org \
--cc=trond.myklebust@hammerspace.com \
--cc=trondmy@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox