Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 30 Sep 2020 02:18:09 +0000 (UTC)
From:      Rick Macklem <rmacklem@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r366278 - head/sys/kern
Message-ID:  <202009300218.08U2I90g094695@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;
 
 	/*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202009300218.08U2I90g094695>