diff options
| author | Andrew Gallatin <gallatin@FreeBSD.org> | 2025-12-09 21:06:20 +0000 |
|---|---|---|
| committer | Andrew Gallatin <gallatin@FreeBSD.org> | 2025-12-12 12:43:33 +0000 |
| commit | f14ca373dde536fe4dbded3da328f79226fbbfa5 (patch) | |
| tree | aad5115dcc20f9b7a68ac81db2c4c4a35ccab942 | |
| parent | cd79149b9e132f7c850a2317f6d3960f0a16b486 (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.c | 44 |
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); |
