From 7d7c4f74f0101fb84c961631eb6c8ea90a41790e Mon Sep 17 00:00:00 2001 From: markj Date: Tue, 28 Apr 2020 15:02:44 +0000 Subject: [PATCH] Make sendfile(SF_SYNC)'s CV wait interruptible. Otherwise, since the CV is not signalled until data is drained from the socket, it is trivial to create an unkillable process using sendfile(SF_SYNC) and a process-private PF_LOCAL socket pair. In particular, the cv_wait() in sendfile() does not get interrupted until data is drained from the receiving socket buffer. Reported by: pho Discussed with: kib MFC after: 2 weeks Sponsored by: The FreeBSD Foundation --- sys/kern/kern_sendfile.c | 55 +++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/sys/kern/kern_sendfile.c b/sys/kern/kern_sendfile.c index 0b72df68eee..93029e765cf 100644 --- a/sys/kern/kern_sendfile.c +++ b/sys/kern/kern_sendfile.c @@ -106,8 +106,36 @@ struct sendfile_sync { struct mtx mtx; struct cv cv; unsigned count; + bool waiting; }; +static void +sendfile_sync_destroy(struct sendfile_sync *sfs) +{ + KASSERT(sfs->count == 0, ("sendfile sync %p still busy", sfs)); + + cv_destroy(&sfs->cv); + mtx_destroy(&sfs->mtx); + free(sfs, M_SENDFILE); +} + +static void +sendfile_sync_signal(struct sendfile_sync *sfs) +{ + mtx_lock(&sfs->mtx); + KASSERT(sfs->count > 0, ("sendfile sync %p not busy", sfs)); + if (--sfs->count == 0) { + if (!sfs->waiting) { + /* The sendfile() waiter was interrupted by a signal. */ + sendfile_sync_destroy(sfs); + return; + } else { + cv_signal(&sfs->cv); + } + } + mtx_unlock(&sfs->mtx); +} + counter_u64_t sfstat[sizeof(struct sfstat) / sizeof(uint64_t)]; static void @@ -153,12 +181,7 @@ sendfile_free_mext(struct mbuf *m) if (m->m_ext.ext_flags & EXT_FLAG_SYNC) { struct sendfile_sync *sfs = m->m_ext.ext_arg2; - - mtx_lock(&sfs->mtx); - KASSERT(sfs->count > 0, ("Sendfile sync botchup count == 0")); - if (--sfs->count == 0) - cv_signal(&sfs->cv); - mtx_unlock(&sfs->mtx); + sendfile_sync_signal(sfs); } } @@ -186,12 +209,7 @@ sendfile_free_mext_pg(struct mbuf *m) if (m->m_ext.ext_flags & EXT_FLAG_SYNC) { struct sendfile_sync *sfs = m->m_ext.ext_arg1; - - mtx_lock(&sfs->mtx); - KASSERT(sfs->count > 0, ("Sendfile sync botchup count == 0")); - if (--sfs->count == 0) - cv_signal(&sfs->cv); - mtx_unlock(&sfs->mtx); + sendfile_sync_signal(sfs); } } @@ -719,6 +737,7 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, sfs = malloc(sizeof(*sfs), M_SENDFILE, M_WAITOK | M_ZERO); mtx_init(&sfs->mtx, "sendfile", NULL, MTX_DEF); cv_init(&sfs->cv, "sendfile"); + sfs->waiting = true; } rem = nbytes ? omin(nbytes, obj_size - offset) : obj_size - offset; @@ -1221,11 +1240,13 @@ vn_sendfile(struct file *fp, int sockfd, struct uio *hdr_uio, if (sfs != NULL) { mtx_lock(&sfs->mtx); if (sfs->count != 0) - cv_wait(&sfs->cv, &sfs->mtx); - KASSERT(sfs->count == 0, ("sendfile sync still busy")); - cv_destroy(&sfs->cv); - mtx_destroy(&sfs->mtx); - free(sfs, M_SENDFILE); + error = cv_wait_sig(&sfs->cv, &sfs->mtx); + if (sfs->count == 0) { + sendfile_sync_destroy(sfs); + } else { + sfs->waiting = false; + mtx_unlock(&sfs->mtx); + } } #ifdef KERN_TLS if (tls != NULL) -- 2.45.0