so_splice: Disallow splicing with KTLS-enabled sockets

Suppose the sink socket in a splice has KTLS enabled.  When data is
transmitted from the source socket, sosend_generic_locked() receives an
mbuf rather than a UIO as it would if userspace were transferring data.
In this case, ktls_frame() expects the mbuf to be unmapped, but in
general this won't be the case.

Simply disallow the combination for now.  Modify so_unsplice() to handle
dismantling a partially initialized splice, in order to simplify error
handling in so_splice().  Make sure that one can't enable KTLS on a
spliced socket, or more specifically, that one can't enable RXTLS on the
source side of a splice, or TXTLS on the sink side of a splice.

Reported by:	[email protected]
Reviewed by:	gallatin
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D49920
This commit is contained in:
Mark Johnston committed 2025-04-22 14:53:34 +00:00
1 parent a1a067b713
commit 1000cc4a0d
2 files changed
+37 -19

No files matched your search

+11 -3
View File
@@ -1332,7 +1332,11 @@ ktls_enable_rx(struct socket *so, struct tls_enable *en)
/* Mark the socket as using TLS offload. */
SOCK_RECVBUF_LOCK(so);
if (__predict_false(so->so_rcv.sb_tls_info != NULL)) {
if (__predict_false(so->so_rcv.sb_tls_info != NULL))
error = EALREADY;
else if ((so->so_rcv.sb_flags & SB_SPLICED) != 0)
error = EINVAL;
if (error != 0) {
SOCK_RECVBUF_UNLOCK(so);
SOCK_IO_RECV_UNLOCK(so);
ktls_free(tls);
@@ -1432,12 +1436,16 @@ ktls_enable_tx(struct socket *so, struct tls_enable *en)
inp = so->so_pcb;
INP_WLOCK(inp);
SOCK_SENDBUF_LOCK(so);
if (__predict_false(so->so_snd.sb_tls_info != NULL)) {
if (__predict_false(so->so_snd.sb_tls_info != NULL))
error = EALREADY;
else if ((so->so_snd.sb_flags & SB_SPLICED) != 0)
error = EINVAL;
if (error != 0) {
SOCK_SENDBUF_UNLOCK(so);
INP_WUNLOCK(inp);
SOCK_IO_SEND_UNLOCK(so);
ktls_free(tls);
return (EALREADY);
return (error);
}
so->so_snd.sb_tls_seqno = be64dec(en->rec_seq);
so->so_snd.sb_tls_info = tls;
+26 -16
View File
@@ -1699,10 +1699,16 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
uma_zfree(splice_zone, sp);
return (error);
}
soref(so);
so->so_splice = sp;
SOCK_RECVBUF_LOCK(so);
if (so->so_rcv.sb_tls_info != NULL) {
SOCK_RECVBUF_UNLOCK(so);
SOCK_UNLOCK(so);
uma_zfree(splice_zone, sp);
return (EINVAL);
}
so->so_rcv.sb_flags |= SB_SPLICED;
so->so_splice = sp;
soref(so);
SOCK_RECVBUF_UNLOCK(so);
SOCK_UNLOCK(so);
@@ -1716,20 +1722,19 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
error = EBUSY;
if (error != 0) {
SOCK_UNLOCK(so2);
SOCK_LOCK(so);
so->so_splice = NULL;
SOCK_RECVBUF_LOCK(so);
so->so_rcv.sb_flags &= ~SB_SPLICED;
SOCK_RECVBUF_UNLOCK(so);
SOCK_UNLOCK(so);
sorele(so);
uma_zfree(splice_zone, sp);
so_unsplice(so, false);
return (error);
}
soref(so2);
so2->so_splice_back = sp;
SOCK_SENDBUF_LOCK(so2);
if (so->so_snd.sb_tls_info != NULL) {
SOCK_SENDBUF_UNLOCK(so2);
SOCK_UNLOCK(so2);
so_unsplice(so, false);
return (EINVAL);
}
so2->so_snd.sb_flags |= SB_SPLICED;
so2->so_splice_back = sp;
soref(so2);
mtx_lock(&sp->mtx);
SOCK_SENDBUF_UNLOCK(so2);
SOCK_UNLOCK(so2);
@@ -1754,7 +1759,7 @@ so_unsplice(struct socket *so, bool timeout)
{
struct socket *so2;
struct so_splice *sp;
bool drain;
bool drain, so2rele;
/*
* First unset SB_SPLICED and hide the splice structure so that
@@ -1793,11 +1798,14 @@ so_unsplice(struct socket *so, bool timeout)
SOCK_LOCK(so2);
KASSERT(!SOLISTENING(so2), ("%s: so2 is listening", __func__));
SOCK_SENDBUF_LOCK(so2);
KASSERT((so2->so_snd.sb_flags & SB_SPLICED) != 0,
KASSERT(sp->state == SPLICE_INIT ||
(so2->so_snd.sb_flags & SB_SPLICED) != 0,
("%s: so2 is not spliced", __func__));
KASSERT(so2->so_splice_back == sp,
KASSERT(sp->state == SPLICE_INIT ||
so2->so_splice_back == sp,
("%s: so_splice_back != sp", __func__));
so2->so_snd.sb_flags &= ~SB_SPLICED;
so2rele = so2->so_splice_back != NULL;
so2->so_splice_back = NULL;
SOCK_SENDBUF_UNLOCK(so2);
SOCK_UNLOCK(so2);
@@ -1815,6 +1823,7 @@ so_unsplice(struct socket *so, bool timeout)
while (sp->state == SPLICE_CLOSING)
msleep(sp, &sp->mtx, PSOCK, "unsplice", 0);
break;
case SPLICE_INIT:
case SPLICE_IDLE:
case SPLICE_EXCEPTION:
sp->state = SPLICE_CLOSED;
@@ -1840,7 +1849,8 @@ so_unsplice(struct socket *so, bool timeout)
CURVNET_SET(so->so_vnet);
sorele(so);
sowwakeup(so2);
sorele(so2);
if (so2rele)
sorele(so2);
CURVNET_RESTORE();
so_splice_free(sp);
return (0);