Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 17 Apr 2020 13:50:27 +0000 (UTC)
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r360041 - stable/12/sys/kern
Message-ID:  <202004171350.03HDoRgF052677@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: markj
Date: Fri Apr 17 13:50:26 2020
New Revision: 360041
URL: https://svnweb.freebsd.org/changeset/base/360041

Log:
  MFC r359778:
  Properly handle disconnected sockets in uipc_ready().

Modified:
  stable/12/sys/kern/uipc_usrreq.c
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/kern/uipc_usrreq.c
==============================================================================
--- stable/12/sys/kern/uipc_usrreq.c	Fri Apr 17 10:12:11 2020	(r360040)
+++ stable/12/sys/kern/uipc_usrreq.c	Fri Apr 17 13:50:26 2020	(r360041)
@@ -773,6 +773,18 @@ uipc_detach(struct socket *so)
 	vplock = NULL;
 	local_unp_rights = 0;
 
+	SOCK_LOCK(so);
+	if (!SOLISTENING(so)) {
+		/*
+		 * Once the socket is removed from the global lists,
+		 * uipc_ready() will not be able to locate its socket buffer, so
+		 * clear the buffer now.  At this point internalized rights have
+		 * already been disposed of.
+		 */
+		sbrelease(&so->so_rcv, so);
+	}
+	SOCK_UNLOCK(so);
+
 	UNP_LINK_WLOCK();
 	LIST_REMOVE(unp, unp_link);
 	unp->unp_gencnt = ++unp_gencnt;
@@ -1241,19 +1253,54 @@ release:
 	return (error);
 }
 
+static bool
+uipc_ready_scan(struct socket *so, struct mbuf *m, int count, int *errorp)
+{
+	struct mbuf *mb, *n;
+	struct sockbuf *sb;
+
+	SOCK_LOCK(so);
+	if (SOLISTENING(so)) {
+		SOCK_UNLOCK(so);
+		return (false);
+	}
+	mb = NULL;
+	sb = &so->so_rcv;
+	SOCKBUF_LOCK(sb);
+	if (sb->sb_fnrdy != NULL) {
+		for (mb = sb->sb_mb, n = mb->m_nextpkt; mb != NULL;) {
+			if (mb == m) {
+				*errorp = sbready(sb, m, count);
+				break;
+			}
+			mb = mb->m_next;
+			if (mb == NULL) {
+				mb = n;
+				n = mb->m_nextpkt;
+			}
+		}
+	}
+	SOCKBUF_UNLOCK(sb);
+	SOCK_UNLOCK(so);
+	return (mb != NULL);
+}
+
 static int
 uipc_ready(struct socket *so, struct mbuf *m, int count)
 {
 	struct unpcb *unp, *unp2;
 	struct socket *so2;
-	int error;
+	int error, i;
 
 	unp = sotounpcb(so);
 
+	KASSERT(so->so_type == SOCK_STREAM,
+	    ("%s: unexpected socket type for %p", __func__, so));
+
 	UNP_PCB_LOCK(unp);
 	if ((unp2 = unp->unp_conn) == NULL) {
 		UNP_PCB_UNLOCK(unp);
-		goto error;
+		goto search;
 	}
 	if (unp != unp2) {
 		if (UNP_PCB_TRYLOCK(unp2) == 0) {
@@ -1261,25 +1308,39 @@ uipc_ready(struct socket *so, struct mbuf *m, int coun
 			UNP_PCB_UNLOCK(unp);
 			UNP_PCB_LOCK(unp2);
 			if (unp_pcb_rele(unp2))
-				goto error;
+				goto search;
 		} else
 			UNP_PCB_UNLOCK(unp);
 	}
 	so2 = unp2->unp_socket;
-
 	SOCKBUF_LOCK(&so2->so_rcv);
 	if ((error = sbready(&so2->so_rcv, m, count)) == 0)
 		sorwakeup_locked(so2);
 	else
 		SOCKBUF_UNLOCK(&so2->so_rcv);
-
 	UNP_PCB_UNLOCK(unp2);
+	return (error);
 
+search:
+	/*
+	 * The receiving socket has been disconnected, but may still be valid.
+	 * In this case, the now-ready mbufs are still present in its socket
+	 * buffer, so perform an exhaustive search before giving up and freeing
+	 * the mbufs.
+	 */
+	UNP_LINK_RLOCK();
+	LIST_FOREACH(unp, &unp_shead, unp_link) {
+		if (uipc_ready_scan(unp->unp_socket, m, count, &error))
+			break;
+	}
+	UNP_LINK_RUNLOCK();
+
+	if (unp == NULL) {
+		for (i = 0; i < count; i++)
+			m = m_free(m);
+		error = ECONNRESET;
+	}
 	return (error);
- error:
-	for (int i = 0; i < count; i++)
-		m = m_free(m);
-	return (ECONNRESET);
 }
 
 static int



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202004171350.03HDoRgF052677>