git: 1000cc4a0d39 - main - so_splice: Disallow splicing with KTLS-enabled sockets
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 22 Apr 2025 15:59:40 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=1000cc4a0d39faeb3e681bb167ed38f164c56604
commit 1000cc4a0d39faeb3e681bb167ed38f164c56604
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-04-22 14:53:34 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-04-22 14:53:34 +0000
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: syzbot+9cc248c4b0ca9b931ab4@syzkaller.appspotmail.com
Reviewed by: gallatin
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D49920
---
sys/kern/uipc_ktls.c | 14 +++++++++++---
sys/kern/uipc_socket.c | 42 ++++++++++++++++++++++++++----------------
2 files changed, 37 insertions(+), 19 deletions(-)
diff --git a/sys/kern/uipc_ktls.c b/sys/kern/uipc_ktls.c
index 6815667594a4..5e9dd254debd 100644
--- a/sys/kern/uipc_ktls.c
+++ b/sys/kern/uipc_ktls.c
@@ -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;
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 291a832189e5..10f81a959147 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -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);