The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=dfad790c8ccad05ff603ceaa5b2efe4205b38e1c
commit dfad790c8ccad05ff603ceaa5b2efe4205b38e1c Author: Konstantin Belousov <[email protected]> AuthorDate: 2026-07-04 02:29:56 +0000 Commit: Konstantin Belousov <[email protected]> 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);
