git: abe8379b4f24 - main - sockets: repair wakeup of accept(2) by shutdown(2)

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Thu, 15 Feb 2024 18:49:03 UTC
The branch main has been updated by glebius:

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

commit abe8379b4f244aa12f13a603124eb6b41faabec5
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2024-02-15 18:48:44 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2024-02-15 18:48:44 +0000

    sockets: repair wakeup of accept(2) by shutdown(2)
    
    That was lost in transition from one-for-all soshutdown() to protocol
    specific methods.  Only protocols that listen(2) were affected.  This is
    not a documented or specified feature, but some software relies on it.  At
    least the FreeSWITCH telephony software uses this behavior on
    PF_INET/SOCK_STREAM.
    
    Fixes:  5bba2728079ed4da33f727dbc2b6ae1de02ba897
---
 sys/kern/uipc_usrreq.c    | 17 ++++++++---------
 sys/netinet/sctp_usrreq.c | 11 +++++------
 sys/netinet/tcp_usrreq.c  | 11 +++++------
 3 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index db226a16674e..20facd9b0a44 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -1669,7 +1669,14 @@ uipc_shutdown(struct socket *so, enum shutdown_how how)
 	int error;
 
 	SOCK_LOCK(so);
-	if ((so->so_state &
+	if (SOLISTENING(so)) {
+		if (how != SHUT_WR) {
+			so->so_error = ECONNABORTED;
+			solisten_wakeup(so);    /* unlocks so */
+		} else
+			SOCK_UNLOCK(so);
+		return (ENOTCONN);
+	} else if ((so->so_state &
 	    (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
 		/*
 		 * POSIX mandates us to just return ENOTCONN when shutdown(2) is
@@ -1691,14 +1698,6 @@ uipc_shutdown(struct socket *so, enum shutdown_how how)
 		}
 	} else
 		error = 0;
-	if (SOLISTENING(so)) {
-		if (how != SHUT_WR) {
-			so->so_error = ECONNABORTED;
-			solisten_wakeup(so);    /* unlocks so */
-		} else
-			SOCK_UNLOCK(so);
-		return (0);
-	}
 	SOCK_UNLOCK(so);
 
 	switch (how) {
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 70fe021be766..3b0da87edce3 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -794,18 +794,17 @@ sctp_shutdown(struct socket *so, enum shutdown_how how)
 		return (EOPNOTSUPP);
 
 	SOCK_LOCK(so);
-	if ((so->so_state &
-	    (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
-		SOCK_UNLOCK(so);
-		return (ENOTCONN);
-	}
 	if (SOLISTENING(so)) {
 		if (how != SHUT_WR) {
 			so->so_error = ECONNABORTED;
 			solisten_wakeup(so);	/* unlocks so */
 		} else
 			SOCK_UNLOCK(so);
-		return (0);
+		return (ENOTCONN);
+	} else if ((so->so_state &
+	    (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
+		SOCK_UNLOCK(so);
+		return (ENOTCONN);
 	}
 	SOCK_UNLOCK(so);
 
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index a283d308801f..9bb953617d99 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -807,18 +807,17 @@ tcp_usr_shutdown(struct socket *so, enum shutdown_how how)
 	int error = 0;
 
 	SOCK_LOCK(so);
-	if ((so->so_state &
-	    (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
-		SOCK_UNLOCK(so);
-		return (ENOTCONN);
-	}
 	if (SOLISTENING(so)) {
 		if (how != SHUT_WR) {
 			so->so_error = ECONNABORTED;
 			solisten_wakeup(so);	/* unlocks so */
 		} else
 			SOCK_UNLOCK(so);
-		return (0);
+		return (ENOTCONN);
+	} else if ((so->so_state &
+	    (SS_ISCONNECTED | SS_ISCONNECTING | SS_ISDISCONNECTING)) == 0) {
+		SOCK_UNLOCK(so);
+		return (ENOTCONN);
 	}
 	SOCK_UNLOCK(so);