Author: rmacklem
Date: Wed Sep 30 02:18:09 2020
New Revision: 366278
URL: https://svnweb.freebsd.org/changeset/base/366278

Log:
  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

Modified:
  head/sys/kern/vfs_vnops.c

Modified: head/sys/kern/vfs_vnops.c
==============================================================================
--- head/sys/kern/vfs_vnops.c   Wed Sep 30 00:56:08 2020        (r366277)
+++ head/sys/kern/vfs_vnops.c   Wed Sep 30 02:18:09 2020        (r366278)
@@ -2790,25 +2790,31 @@ vn_copy_file_range(struct vnode *invp, off_t *inoffp, 
 {
        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;
 
        /*
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to