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