Make copy_file_range(2) Linux compatible for overflow of offset + len.

Without this patch, if a call to copy_file_range(2) specifies an input file
offset + len that would wrap around, EINVAL is returned.
I thought that was the Linux behaviour, but recent testing showed that
Linux accepts this case and does the copy_file_range() to EOF.

This patch changes the FreeBSD code to exhibit the same behaviour as
Linux for this case.

Reviewed by:	asomers, kib
Differential Revision:	https://reviews.freebsd.org/D26569
This commit is contained in:
Rick Macklem 2020-09-30 02:18:09 +00:00
parent 55be47b894
commit 164aa1e941
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=366278

View File

@ -2790,27 +2790,33 @@ vn_copy_file_range(struct vnode *invp, off_t *inoffp, struct vnode *outvp,
{
int error;
size_t len;
uint64_t uvalin, uvalout;
uint64_t uval;
len = *lenp;
*lenp = 0; /* For error returns. */
error = 0;
/* Do some sanity checks on the arguments. */
uvalin = *inoffp;
uvalin += len;
uvalout = *outoffp;
uvalout += len;
if (invp->v_type == VDIR || outvp->v_type == VDIR)
error = EISDIR;
else if (*inoffp < 0 || uvalin > INT64_MAX || uvalin <
(uint64_t)*inoffp || *outoffp < 0 || uvalout > INT64_MAX ||
uvalout < (uint64_t)*outoffp || invp->v_type != VREG ||
outvp->v_type != VREG)
else if (*inoffp < 0 || *outoffp < 0 ||
invp->v_type != VREG || outvp->v_type != VREG)
error = EINVAL;
if (error != 0)
goto out;
/* Ensure offset + len does not wrap around. */
uval = *inoffp;
uval += len;
if (uval > INT64_MAX)
len = INT64_MAX - *inoffp;
uval = *outoffp;
uval += len;
if (uval > INT64_MAX)
len = INT64_MAX - *outoffp;
if (len == 0)
goto out;
/*
* If the two vnode are for the same file system, call
* VOP_COPY_FILE_RANGE(), otherwise call vn_generic_copy_file_range()