git: 9d9fa9a2c22f - main - unix: Fix handling of listening sockets during garbage collection

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Fri, 14 Nov 2025 00:46:15 UTC
The branch main has been updated by markj:

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

commit 9d9fa9a2c22f67d5f8afec18106c9f0072d6b3d4
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-11-13 22:56:15 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2025-11-14 00:43:38 +0000

    unix: Fix handling of listening sockets during garbage collection
    
    socantrcvmore() and unp_dispose() assume that the socket's socket
    buffers are initialized, which isn't the case for listening sockets.
    
    Reported by:    syzbot+a62883292a5c257703be@syzkaller.appspotmail.com
    MFC after:      1 week
    Reviewed by:    glebius
    Differential Revision:  https://reviews.freebsd.org/D53743
---
 sys/kern/uipc_usrreq.c            | 10 ++++++----
 tests/sys/kern/unix_passfd_test.c | 29 +++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 807271488af2..8c9fa4c012e4 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -4208,10 +4208,12 @@ unp_gc(__unused void *arg, int pending)
 		struct socket *so;
 
 		so = unref[i]->f_data;
-		CURVNET_SET(so->so_vnet);
-		socantrcvmore(so);
-		unp_dispose(so);
-		CURVNET_RESTORE();
+		if (!SOLISTENING(so)) {
+			CURVNET_SET(so->so_vnet);
+			socantrcvmore(so);
+			unp_dispose(so);
+			CURVNET_RESTORE();
+		}
 	}
 
 	/*
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c
index 7dc4541ad402..66bb406ea14e 100644
--- a/tests/sys/kern/unix_passfd_test.c
+++ b/tests/sys/kern/unix_passfd_test.c
@@ -1189,6 +1189,34 @@ ATF_TC_CLEANUP(cross_jail_dirfd, tc)
 		err(1, "jail_remove");
 }
 
+ATF_TC_WITHOUT_HEAD(listening_socket);
+ATF_TC_BODY(listening_socket, tc)
+{
+	struct sockaddr_un sun;
+	int error, ls, s[2];
+
+	ls = socket(AF_UNIX, SOCK_STREAM, 0);
+	ATF_REQUIRE(ls != -1);
+
+	memset(&sun, 0, sizeof(sun));
+	sun.sun_len = sizeof(sun);
+	sun.sun_family = AF_UNIX;
+	snprintf(sun.sun_path, sizeof(sun.sun_path), "listen.sock");
+	error = bind(ls, (struct sockaddr *)&sun, sizeof(sun));
+	ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+	error = listen(ls, 0);
+
+	error = socketpair(AF_UNIX, SOCK_STREAM, 0, s);
+	ATF_REQUIRE_MSG(error == 0, "socketpair failed: %s", strerror(errno));
+
+	sendfd(s[0], ls);
+	sendfd(s[0], s[0]);
+	sendfd(s[0], s[1]);
+	close(ls);
+	close(s[0]);
+	close(s[1]);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 
@@ -1211,6 +1239,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, empty_rights_message);
 	ATF_TP_ADD_TC(tp, control_creates_records);
 	ATF_TP_ADD_TC(tp, cross_jail_dirfd);
+	ATF_TP_ADD_TC(tp, listening_socket);
 
 	return (atf_no_error());
 }