git: dfad790c8cca - main - sendfile: stop abusing kern_writev()
Date: Tue, 07 Jul 2026 21:47:35 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=dfad790c8ccad05ff603ceaa5b2efe4205b38e1c
commit dfad790c8ccad05ff603ceaa5b2efe4205b38e1c
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-07-04 02:29:56 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-07-07 21:47:18 +0000
sendfile: stop abusing kern_writev()
Provide convenient wrapper kern_filewrite() around fo_write().
Switch to use it in vn_sendfile(). This allows to avoid duplicate
fget() when we already have the reference to the file, which creates a
correctness race with the userspace. Also td_retval[0] clearing hack
can be removed.
Reviewed by: glebius, markj
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D58035
---
sys/kern/kern_sendfile.c | 25 +++++++++++++------------
sys/kern/sys_generic.c | 41 ++++++++++++++++++++++++++++++-----------
sys/sys/syscallsubr.h | 2 ++
3 files changed, 45 insertions(+), 23 deletions(-)
diff --git a/sys/kern/kern_sendfile.c b/sys/kern/kern_sendfile.c
index cf9716560c07..1a534210ca0c 100644
--- a/sys/kern/kern_sendfile.c
+++ b/sys/kern/kern_sendfile.c
@@ -1168,14 +1168,17 @@ prepend_header:
}
/*
- * Send trailers. Wimp out and use writev(2).
+ * Send trailers.
*/
if (trl_uio != NULL) {
+ ssize_t cnt;
+
SOCK_IO_SEND_UNLOCK(so);
CURVNET_RESTORE();
- error = kern_writev(td, sockfd, trl_uio);
+ error = kern_filewrite(td, sockfd, sock_fp, trl_uio,
+ (off_t)-1, 0, &cnt);
if (error == 0)
- sbytes += td->td_retval[0];
+ sbytes += cnt;
goto out;
}
@@ -1183,15 +1186,8 @@ done:
SOCK_IO_SEND_UNLOCK(so);
CURVNET_RESTORE();
out:
- /*
- * If there was no error we have to clear td->td_retval[0]
- * because it may have been set by writev.
- */
- if (error == 0) {
- td->td_retval[0] = 0;
- if (sbytes > 0 && vp != NULL)
- INOTIFY(vp, IN_ACCESS);
- }
+ if (error == 0 && sbytes > 0 && vp != NULL)
+ INOTIFY(vp, IN_ACCESS);
if (sent != NULL) {
(*sent) = sbytes;
}
@@ -1260,6 +1256,11 @@ sendfile(struct thread *td, struct sendfile_args *uap, int compat)
&trl_uio);
if (error != 0)
goto out;
+ if (trl_uio->uio_rw != UIO_WRITE) {
+ error = EINVAL;
+ goto out;
+ }
+ trl_uio->uio_td = td;
}
}
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
index b84f675d1dcb..07b98613e087 100644
--- a/sys/kern/sys_generic.c
+++ b/sys/kern/sys_generic.c
@@ -549,21 +549,16 @@ dofilewrite(struct thread *td, int fd, struct file *fp, struct uio *auio,
{
ssize_t cnt;
int error;
-#ifdef KTRACE
- struct uio *ktruio = NULL;
-#endif
AUDIT_ARG_FD(fd);
+
auio->uio_rw = UIO_WRITE;
auio->uio_td = td;
auio->uio_offset = offset;
-#ifdef KTRACE
- if (KTRPOINT(td, KTR_GENIO))
- ktruio = cloneuio(auio);
-#endif
- cnt = auio->uio_resid;
- error = fo_write(fp, auio, td->td_ucred, flags, td);
+ error = kern_filewrite(td, fd, fp, auio, offset, flags, &cnt);
+
/*
+ * Handle short writes and generate SIGPIPE if needed.
* Socket layer is responsible for special error handling,
* see sousrsend().
*/
@@ -577,15 +572,39 @@ dofilewrite(struct thread *td, int fd, struct file *fp, struct uio *auio,
PROC_UNLOCK(td->td_proc);
}
}
- cnt -= auio->uio_resid;
+
+ if (error == 0)
+ td->td_retval[0] = cnt;
+ return (error);
+}
+
+/*
+ * Write io request specified by auio into the file fp. If fd != -1,
+ * might generate the ktrace io point.
+ */
+int
+kern_filewrite(struct thread *td, int fd, struct file *fp, struct uio *auio,
+ off_t offset, int flags, ssize_t *cntp)
+{
+ ssize_t cnt;
+ int error;
+#ifdef KTRACE
+ struct uio *ktruio;
+
+ ktruio = fd != -1 && KTRPOINT(td, KTR_GENIO) ? cloneuio(auio) : NULL;
+#endif
+ cnt = auio->uio_resid;
+ error = fo_write(fp, auio, td->td_ucred, flags, td);
#ifdef KTRACE
if (ktruio != NULL) {
+ cnt -= auio->uio_resid;
if (error == 0)
ktruio->uio_resid = cnt;
ktrgenio(fd, UIO_WRITE, ktruio, error);
}
#endif
- td->td_retval[0] = cnt;
+
+ *cntp = cnt;
return (error);
}
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
index c6bfe89dcf35..131a8925a2d4 100644
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -176,6 +176,8 @@ int kern_fcntl_freebsd(struct thread *td, int fd, int cmd, intptr_t arg);
int kern_fhopen(struct thread *td, const struct fhandle *u_fhp, int flags);
int kern_fhstat(struct thread *td, fhandle_t fh, struct stat *buf);
int kern_fhstatfs(struct thread *td, fhandle_t fh, struct statfs *buf);
+int kern_filewrite(struct thread *td, int fd, struct file *fp,
+ struct uio *auio, off_t offset, int flags, ssize_t *cntp);
int kern_fpathconf(struct thread *td, int fd, int name, long *valuep);
int kern_freebsd11_getfsstat(struct thread *td,
struct freebsd11_statfs *ubuf, long bufsize, int mode);