git: 992b18a9ec9f - main - so_splice: Synchronize so_unsplice() with so_splice()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 15 Apr 2025 00:57:30 UTC
The branch main has been updated by markj:

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

commit 992b18a9ec9f21cdf15b70b2f510fa2a69844385
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-04-15 00:55:00 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-04-15 00:55:00 +0000

    so_splice: Synchronize so_unsplice() with so_splice()
    
    so_unsplice() assumed that if SB_SPLICED is set in the receive buffer of
    the first socket, then the splice is fully initialized.  However, that's
    not true, and it's possible for so_unsplice() to race ahead of
    so_splice().
    
    Modify so_unsplice() to simply bail if the splice state is embryonic.
    
    Reported by:    syzkaller
    Reviewed by:    gallatin
    Fixes:          a1da7dc1cdad ("socket: Implement SO_SPLICE")
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D49814
---
 sys/kern/uipc_socket.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index ac00696236a5..291a832189e5 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1742,6 +1742,8 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
 	/*
 	 * Transfer any data already present in the socket buffer.
 	 */
+	KASSERT(sp->state == SPLICE_INIT,
+	    ("so_splice: splice %p state %d", sp, sp->state));
 	sp->state = SPLICE_QUEUED;
 	so_splice_xfer(sp);
 	return (0);
@@ -1770,8 +1772,19 @@ so_unsplice(struct socket *so, bool timeout)
 		SOCK_UNLOCK(so);
 		return (ENOTCONN);
 	}
-	so->so_rcv.sb_flags &= ~SB_SPLICED;
 	sp = so->so_splice;
+	mtx_lock(&sp->mtx);
+	if (sp->state == SPLICE_INIT) {
+		/*
+		 * A splice is in the middle of being set up.
+		 */
+		mtx_unlock(&sp->mtx);
+		SOCK_RECVBUF_UNLOCK(so);
+		SOCK_UNLOCK(so);
+		return (ENOTCONN);
+	}
+	mtx_unlock(&sp->mtx);
+	so->so_rcv.sb_flags &= ~SB_SPLICED;
 	so->so_splice = NULL;
 	SOCK_RECVBUF_UNLOCK(so);
 	SOCK_UNLOCK(so);