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
This commit is contained in:
markj 2020-04-28 15:02:44 +00:00
parent 277d224e09
commit 7d7c4f74f0

View File

@ -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)