git: e7044084cf81 - stable/13 - vfs_vnops.c: Fix vn_generic_copy_file_range() for truncation
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 03 Jan 2024 01:36:35 UTC
The branch stable/13 has been updated by rmacklem:
URL: https://cgit.FreeBSD.org/src/commit/?id=e7044084cf813bfb66cbea8e9278895b26eda5d2
commit e7044084cf813bfb66cbea8e9278895b26eda5d2
Author: Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2023-12-31 23:55:24 +0000
Commit: Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2024-01-03 01:34:31 +0000
vfs_vnops.c: Fix vn_generic_copy_file_range() for truncation
When copy_file_range(2) was first being developed,
*inoffp + len had to be <= infile_size or an error was
returned. This semantic (as defined by Linux) changed
to allow *inoffp + len to be greater than infile_size and
the copy would end at *inoffp + infile_size.
Unfortunately, the code that decided if the outfd should
be truncated in length did not get updated for this
semantics change.
As such, if a copy_file_range(2) is done, where infile_size - *inoffp
is less that outfile_size but len is large, the outfd file is truncated
when it should not be. (The semantics for this for Linux is to not
truncate outfd in this case.)
This patch fixes the problem. I believe the calculation is safe
for all non-negative values of outsize, *outoffp, *inoffp and insize,
which should be ok, since they are all guaranteed to be non-negative.
Note that this bug is not observed over NFSv4.2, since it truncates
len to infile_size - *inoffp.
PR: 276045
(cherry picked from commit 2319ca6a01816f7fc85d623097c639f239e18c6a)
---
sys/kern/vfs_vnops.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index e720ef151c9e..f5961a33f960 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -3318,8 +3318,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
goto out;
if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0)
holein = 0;
- if (holein > 0)
- error = VOP_GETATTR(invp, &inva, incred);
+ error = VOP_GETATTR(invp, &inva, incred);
VOP_UNLOCK(invp);
if (error != 0)
goto out;
@@ -3355,8 +3354,11 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
*/
if (error == 0)
error = VOP_GETATTR(outvp, &va, outcred);
- if (error == 0 && va.va_size > *outoffp && va.va_size <=
- *outoffp + len) {
+ if (error == 0 && va.va_size > *outoffp &&
+ *outoffp <= OFF_MAX - len && va.va_size <= *outoffp + len &&
+ *inoffp < inva.va_size &&
+ *outoffp <= OFF_MAX - (inva.va_size - *inoffp) &&
+ outsize <= *outoffp + (inva.va_size - *inoffp)) {
#ifdef MAC
error = mac_vnode_check_write(curthread->td_ucred,
outcred, outvp);