Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 09 Dec 2025 22:20:31 +0000
From:      Andrew Gallatin <gallatin@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: a837d1fe49e0 - main - splice: Fix leaks that can happen when initiating a splice
Message-ID:  <6938a0af.3d667.4486402f@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help

The branch main has been updated by gallatin:

URL: https://cgit.FreeBSD.org/src/commit/?id=a837d1fe49e0255d81c670dc271ff245ae960097

commit a837d1fe49e0255d81c670dc271ff245ae960097
Author:     Andrew Gallatin <gallatin@FreeBSD.org>
AuthorDate: 2025-12-09 21:06:20 +0000
Commit:     Andrew Gallatin <gallatin@FreeBSD.org>
CommitDate: 2025-12-09 22:07:11 +0000

    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
---
 sys/kern/uipc_socket.c | 44 +++++++++++++++++++++++++++-----------------
 1 file 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);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6938a0af.3d667.4486402f>