git: c1e733258770 - main - sendfile(2): rework compat implementation

From: Brooks Davis <brooks_at_FreeBSD.org>
Date: Sun, 13 Sep 2026 08:42:38 UTC
The branch main has been updated by brooks:

URL: https://cgit.FreeBSD.org/src/commit/?id=c1e73325877038655236930534253572ec4a0317

commit c1e73325877038655236930534253572ec4a0317
Author:     Brooks Davis <brooks@FreeBSD.org>
AuthorDate: 2026-09-13 08:41:38 +0000
Commit:     Brooks Davis <brooks@FreeBSD.org>
CommitDate: 2026-09-13 08:41:59 +0000

    sendfile(2): rework compat implementation
    
    Rename the sendfile() function to kern_sendfile(), expand the arguments
    previously passed in struct sendfile_args, and extend with two function
    pointer arguments to copy in the header/trailer structure and the
    create uio's for the header and trailer as required.  Use this to allow
    the removal of freebsd32_do_sendfile() which was a nearly identical
    duplicate of sendfile() with attendant maintenance cost.
    
    Reviewed by:    kib, markj
    Effort:         CHERI upstreaming
    Sponsored by:   Innovate UK
    Differential Revision:  https://reviews.freebsd.org/D59034
---
 sys/compat/freebsd32/freebsd32_misc.c | 93 +++++++----------------------------
 sys/kern/kern_sendfile.c              | 57 ++++++++++-----------
 sys/sys/syscallsubr.h                 | 18 +++++++
 3 files changed, 64 insertions(+), 104 deletions(-)

diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index bb4548660278..7b4b0cdcfa4b 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -2222,83 +2222,20 @@ struct sf_hdtr32 {
 };
 
 static int
-freebsd32_do_sendfile(struct thread *td,
-    struct freebsd32_sendfile_args *uap, int compat)
+freebsd32_copyin_hdtr(const void *uhdtr, struct sf_hdtr *hdtr)
 {
 	struct sf_hdtr32 hdtr32;
-	struct sf_hdtr hdtr;
-	struct uio *hdr_uio, *trl_uio;
-	struct file *fp;
-	cap_rights_t rights;
-	struct iovec32 *iov32;
-	off_t offset, sbytes;
 	int error;
 
-	offset = PAIR32TO64(off_t, uap->offset);
-	if (offset < 0)
-		return (EINVAL);
-
-	hdr_uio = trl_uio = NULL;
-
-	if (uap->hdtr != NULL) {
-		error = copyin(uap->hdtr, &hdtr32, sizeof(hdtr32));
-		if (error)
-			goto out;
-		PTRIN_CP(hdtr32, hdtr, headers);
-		CP(hdtr32, hdtr, hdr_cnt);
-		PTRIN_CP(hdtr32, hdtr, trailers);
-		CP(hdtr32, hdtr, trl_cnt);
-
-		if (hdtr.headers != NULL) {
-			iov32 = PTRIN(hdtr32.headers);
-			error = freebsd32_copyinuio(iov32,
-			    hdtr32.hdr_cnt, &hdr_uio);
-			if (error)
-				goto out;
-#ifdef COMPAT_FREEBSD4
-			/*
-			 * In FreeBSD < 5.0 the nbytes to send also included
-			 * the header.  If compat is specified subtract the
-			 * header size from nbytes.
-			 */
-			if (compat) {
-				if (uap->nbytes > hdr_uio->uio_resid)
-					uap->nbytes -= hdr_uio->uio_resid;
-				else
-					uap->nbytes = 0;
-			}
-#endif
-		}
-		if (hdtr.trailers != NULL) {
-			iov32 = PTRIN(hdtr32.trailers);
-			error = freebsd32_copyinuio(iov32,
-			    hdtr32.trl_cnt, &trl_uio);
-			if (error)
-				goto out;
-			trl_uio->uio_rw = UIO_WRITE;
-			trl_uio->uio_td = td;
-		}
-	}
-
-	AUDIT_ARG_FD(uap->fd);
-
-	if ((error = fget_read(td, uap->fd,
-	    cap_rights_init_one(&rights, CAP_PREAD), &fp)) != 0)
-		goto out;
-
-	error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, offset,
-	    uap->nbytes, &sbytes, uap->flags, td);
-	fdrop(fp, td);
-
-	if (uap->sbytes != NULL)
-		(void)copyout(&sbytes, uap->sbytes, sizeof(off_t));
+	error = copyin(uhdtr, &hdtr32, sizeof(hdtr32));
+	if (error != 0)
+		return (error);
+	hdtr->headers = PTRIN(hdtr32.headers);
+	hdtr->hdr_cnt = hdtr32.hdr_cnt;
+	hdtr->trailers = PTRIN(hdtr32.trailers);
+	hdtr->trl_cnt = hdtr32.trl_cnt;
 
-out:
-	if (hdr_uio)
-		freeuio(hdr_uio);
-	if (trl_uio)
-		freeuio(trl_uio);
-	return (error);
+	return (0);
 }
 
 #ifdef COMPAT_FREEBSD4
@@ -2306,16 +2243,20 @@ int
 freebsd4_freebsd32_sendfile(struct thread *td,
     struct freebsd4_freebsd32_sendfile_args *uap)
 {
-	return (freebsd32_do_sendfile(td,
-	    (struct freebsd32_sendfile_args *)uap, 1));
+	return (kern_sendfile(td, uap->fd, uap->s,
+	    PAIR32TO64(off_t, uap->offset), uap->nbytes,
+	    (struct sf_hdtr *)uap->hdtr, uap->sbytes, uap->flags,
+	    true, freebsd32_copyin_hdtr, (copyinuio_t *)freebsd32_copyinuio));
 }
 #endif
 
 int
 freebsd32_sendfile(struct thread *td, struct freebsd32_sendfile_args *uap)
 {
-
-	return (freebsd32_do_sendfile(td, uap, 0));
+	return (kern_sendfile(td, uap->fd, uap->s,
+	    PAIR32TO64(off_t, uap->offset), uap->nbytes,
+	    (struct sf_hdtr *)uap->hdtr, uap->sbytes, uap->flags,
+	    false, freebsd32_copyin_hdtr, (copyinuio_t *)freebsd32_copyinuio));
 }
 
 static void
diff --git a/sys/kern/kern_sendfile.c b/sys/kern/kern_sendfile.c
index f975470e6dcf..b4ad28b27b36 100644
--- a/sys/kern/kern_sendfile.c
+++ b/sys/kern/kern_sendfile.c
@@ -1209,7 +1209,15 @@ out:
 }
 
 static int
-sendfile(struct thread *td, struct sendfile_args *uap, int compat)
+copyin_hdtr(const struct sf_hdtr *uhdtr, struct sf_hdtr *hdtr)
+{
+	return (copyin(uhdtr, hdtr, sizeof(*hdtr)));
+}
+
+int
+kern_sendfile(struct thread *td, int fd, int s, off_t offset, size_t nbytes,
+    struct sf_hdtr *uhdtr, off_t *usbytes, int flags, bool compat,
+    copyin_hdtr_t *copyin_hdtr_f, copyinuio_t *copyinuio_f)
 {
 	struct sf_hdtr hdtr;
 	struct uio *hdr_uio, *trl_uio;
@@ -1221,18 +1229,18 @@ sendfile(struct thread *td, struct sendfile_args *uap, int compat)
 	 * File offset must be positive.  If it goes beyond EOF
 	 * we send only the header/trailer and no payload data.
 	 */
-	if (uap->offset < 0)
+	if (offset < 0)
 		return (EINVAL);
 
 	sbytes = 0;
 	hdr_uio = trl_uio = NULL;
 
-	if (uap->hdtr != NULL) {
-		error = copyin(uap->hdtr, &hdtr, sizeof(hdtr));
+	if (uhdtr != NULL) {
+		error = copyin_hdtr_f(uhdtr, &hdtr);
 		if (error != 0)
 			goto out;
 		if (hdtr.headers != NULL) {
-			error = copyinuio(hdtr.headers, hdtr.hdr_cnt,
+			error = copyinuio_f(hdtr.headers, hdtr.hdr_cnt,
 			    &hdr_uio);
 			if (error != 0)
 				goto out;
@@ -1243,15 +1251,15 @@ sendfile(struct thread *td, struct sendfile_args *uap, int compat)
 			 * header size from nbytes.
 			 */
 			if (compat) {
-				if (uap->nbytes > hdr_uio->uio_resid)
-					uap->nbytes -= hdr_uio->uio_resid;
+				if (nbytes > hdr_uio->uio_resid)
+					nbytes -= hdr_uio->uio_resid;
 				else
-					uap->nbytes = 0;
+					nbytes = 0;
 			}
 #endif
 		}
 		if (hdtr.trailers != NULL) {
-			error = copyinuio(hdtr.trailers, hdtr.trl_cnt,
+			error = copyinuio_f(hdtr.trailers, hdtr.trl_cnt,
 			    &trl_uio);
 			if (error != 0)
 				goto out;
@@ -1260,21 +1268,21 @@ sendfile(struct thread *td, struct sendfile_args *uap, int compat)
 		}
 	}
 
-	AUDIT_ARG_FD(uap->fd);
+	AUDIT_ARG_FD(fd);
 
 	/*
 	 * sendfile(2) can start at any offset within a file so we require
 	 * CAP_READ+CAP_SEEK = CAP_PREAD.
 	 */
-	if ((error = fget_read(td, uap->fd, &cap_pread_rights, &fp)) != 0)
+	if ((error = fget_read(td, fd, &cap_pread_rights, &fp)) != 0)
 		goto out;
 
-	error = fo_sendfile(fp, uap->s, hdr_uio, trl_uio, uap->offset,
-	    uap->nbytes, &sbytes, uap->flags, td);
+	error = fo_sendfile(fp, s, hdr_uio, trl_uio, offset,
+	    nbytes, &sbytes, flags, td);
 	fdrop(fp, td);
 
-	if (uap->sbytes != NULL)
-		(void)copyout(&sbytes, uap->sbytes, sizeof(off_t));
+	if (usbytes != NULL)
+		(void)copyout(&sbytes, usbytes, sizeof(off_t));
 
 out:
 	freeuio(hdr_uio);
@@ -1296,24 +1304,17 @@ out:
 int
 sys_sendfile(struct thread *td, struct sendfile_args *uap)
 {
-
-	return (sendfile(td, uap, 0));
+	return (kern_sendfile(td, uap->fd, uap->s, uap->offset,
+	    uap->nbytes, uap->hdtr, uap->sbytes, uap->flags, false,
+	    (copyin_hdtr_t *)copyin_hdtr, (copyinuio_t *)copyinuio));
 }
 
 #ifdef COMPAT_FREEBSD4
 int
 freebsd4_sendfile(struct thread *td, struct freebsd4_sendfile_args *uap)
 {
-	struct sendfile_args args;
-
-	args.fd = uap->fd;
-	args.s = uap->s;
-	args.offset = uap->offset;
-	args.nbytes = uap->nbytes;
-	args.hdtr = uap->hdtr;
-	args.sbytes = uap->sbytes;
-	args.flags = uap->flags;
-
-	return (sendfile(td, &args, 1));
+	return (kern_sendfile(td, uap->fd, uap->s, uap->offset,
+	    uap->nbytes, uap->hdtr, uap->sbytes, uap->flags, true,
+	    (copyin_hdtr_t *)copyin_hdtr, (copyinuio_t *)copyinuio));
 }
 #endif /* COMPAT_FREEBSD4 */
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index 190dd41ce6ee..3a30c9cd984d 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -82,6 +82,20 @@ struct mmap_req {
 	mmap_check_fp_fn	mr_check_fp_fn;
 };
 
+/*
+ * A copyin_hdtr_t takes a pointer to a sendfile header/trailer in
+ * userspace and storage for on in the kernel and copies it in.
+ */
+typedef int (copyin_hdtr_t)(const void *hdtrp, struct sf_hdtr *hdtr);
+
+/*
+ * A copyinuio_t takes a pointer to an iovec in userspace along with a
+ * count and allocates a struct uio containing a copy of the iovec.
+ * The uio should be freed with freeuio().
+ */
+typedef int (copyinuio_t)(const void *iovp, unsigned int iovcnt,
+    struct uio **iov);
+
 uint64_t at2cnpflags(u_int at_flags, u_int mask);
 int	kern___getcwd(struct thread *td, char *buf, enum uio_seg bufseg,
 	    size_t buflen, size_t path_max);
@@ -334,6 +348,10 @@ int	kern_sched_rr_get_interval_td(struct thread *td, struct thread *targettd,
 	    struct timespec *ts);
 int	kern_semctl(struct thread *td, int semid, int semnum, int cmd,
 	    union semun *arg, register_t *rval);
+int	kern_sendfile(struct thread *td, int fd, int s, off_t offset,
+	    size_t nbytes, struct sf_hdtr *hdtr, off_t *sbytes, int flags,
+	    bool compat, copyin_hdtr_t *copyin_hdtr_f,
+	    copyinuio_t *copyinuio_f);
 int	kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou,
 	    fd_set *fd_ex, struct timeval *tvp, int abi_nfdbits);
 int	kern_sendit(struct thread *td, int s, struct msghdr *mp, int flags,