aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Gallatin <gallatin@FreeBSD.org>2025-12-09 21:06:20 +0000
committerAndrew Gallatin <gallatin@FreeBSD.org>2025-12-12 12:43:33 +0000
commitf14ca373dde536fe4dbded3da328f79226fbbfa5 (patch)
treeaad5115dcc20f9b7a68ac81db2c4c4a35ccab942
parentcd79149b9e132f7c850a2317f6d3960f0a16b486 (diff)
splice: Fix leaks that can happen when initiating a splice
- change the state to SPLICE_EXCEPTION to allow so_unsplice() to work to cleanup failed splices (fixes socket reference leak) - NULL out sp->dst when unsplicing from so_splice() before so2 has been been referenced. - Deal with a null sp->dst / so2 in so_unsplice - Fix asserts that talked about sp->state == SPLICE_INIT; that state is not possible here. Differential Revision: https://reviews.freebsd.org/D54157 Reviewed by: markj Sponsored by: Netflix Fixes: c0c5d01e5374 ("so_splice: Synchronize so_unsplice() with so_splice()") MFC after: 3 days (cherry picked from commit a837d1fe49e0255d81c670dc271ff245ae960097)
-rw-r--r--sys/kern/uipc_socket.c44
1 files changed, 27 insertions, 17 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 00aa5f9309b2..9eba3ad2e082 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1726,6 +1726,10 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
error = EBUSY;
if (error != 0) {
SOCK_UNLOCK(so2);
+ mtx_lock(&sp->mtx);
+ sp->dst = NULL;
+ sp->state = SPLICE_EXCEPTION;
+ mtx_unlock(&sp->mtx);
so_unsplice(so, false);
return (error);
}
@@ -1733,6 +1737,10 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
if (so->so_snd.sb_tls_info != NULL) {
SOCK_SENDBUF_UNLOCK(so2);
SOCK_UNLOCK(so2);
+ mtx_lock(&sp->mtx);
+ sp->dst = NULL;
+ sp->state = SPLICE_EXCEPTION;
+ mtx_unlock(&sp->mtx);
so_unsplice(so, false);
return (EINVAL);
}
@@ -1799,20 +1807,20 @@ so_unsplice(struct socket *so, bool timeout)
SOCK_UNLOCK(so);
so2 = sp->dst;
- SOCK_LOCK(so2);
- KASSERT(!SOLISTENING(so2), ("%s: so2 is listening", __func__));
- SOCK_SENDBUF_LOCK(so2);
- KASSERT(sp->state == SPLICE_INIT ||
- (so2->so_snd.sb_flags & SB_SPLICED) != 0,
- ("%s: so2 is not spliced", __func__));
- 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);
+ if (so2 != NULL) {
+ SOCK_LOCK(so2);
+ KASSERT(!SOLISTENING(so2), ("%s: so2 is listening", __func__));
+ SOCK_SENDBUF_LOCK(so2);
+ KASSERT((so2->so_snd.sb_flags & SB_SPLICED) != 0,
+ ("%s: so2 is not spliced", __func__));
+ KASSERT(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);
+ }
/*
* No new work is being enqueued. The worker thread might be
@@ -1852,9 +1860,11 @@ so_unsplice(struct socket *so, bool timeout)
sorwakeup(so);
CURVNET_SET(so->so_vnet);
sorele(so);
- sowwakeup(so2);
- if (so2rele)
- sorele(so2);
+ if (so2 != NULL) {
+ sowwakeup(so2);
+ if (so2rele)
+ sorele(so2);
+ }
CURVNET_RESTORE();
so_splice_free(sp);
return (0);