--- old/usr/src/uts/common/inet/ip/ipclassifier.c	Thu Aug 27 14:38:32 2020
+++ new/usr/src/uts/common/inet/ip/ipclassifier.c	Thu Aug 27 14:38:32 2020
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2020 Joyent, Inc.
  */
 
 /*
@@ -2745,7 +2746,11 @@
 		return (NULL);
 	}
 
-	mutex_exit(&connp->conn_lock);
+	/*
+	 * Continue to hold conn_lock because we don't want to race with an
+	 * in-progress close, which will have set-to-NULL (and destroyed
+	 * upper_handle, aka sonode (and vnode)) BEFORE setting CONN_CLOSING.
+	 */
 
 	if (connp->conn_upper_handle != NULL) {
 		vn = (*connp->conn_upcalls->su_get_vnode)
@@ -2757,6 +2762,8 @@
 		flags |= MIB2_SOCKINFO_STREAM;
 	}
 
+	mutex_exit(&connp->conn_lock);
+
 	if (vn == NULL || VOP_GETATTR(vn, &attr, 0, CRED(), NULL) != 0) {
 		if (vn != NULL)
 			VN_RELE(vn);
--- old/usr/src/uts/common/inet/tcp/tcp.c	Thu Aug 27 14:38:33 2020
+++ new/usr/src/uts/common/inet/tcp/tcp.c	Thu Aug 27 14:38:33 2020
@@ -21,10 +21,10 @@
 
 /*
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2019 Joyent, Inc.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2013, 2017 by Delphix. All rights reserved.
  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2020 Joyent, Inc.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
@@ -1019,10 +1019,23 @@
 
 	/* If we have an upper handle (socket), release it */
 	if (IPCL_IS_NONSTR(connp)) {
-		ASSERT(connp->conn_upper_handle != NULL);
-		(*connp->conn_upcalls->su_closed)(connp->conn_upper_handle);
+		sock_upcalls_t *upcalls = connp->conn_upcalls;
+		sock_upper_handle_t handle = connp->conn_upper_handle;
+
+		ASSERT(upcalls != NULL);
+		ASSERT(upcalls->su_closed != NULL);
+		ASSERT(handle != NULL);
+		/*
+		 * Set these to NULL first because closed() will free upper
+		 * structures.  Acquire conn_lock because an external caller
+		 * like conn_get_socket_info() will upcall if these are
+		 * non-NULL.
+		 */
+		mutex_enter(&connp->conn_lock);
 		connp->conn_upper_handle = NULL;
 		connp->conn_upcalls = NULL;
+		mutex_exit(&connp->conn_lock);
+		upcalls->su_closed(handle);
 	}
 }
 
@@ -1421,13 +1434,26 @@
 	 * nothing to do other than clearing the field.
 	 */
 	if (connp->conn_upper_handle != NULL) {
+		sock_upcalls_t *upcalls = connp->conn_upcalls;
+		sock_upper_handle_t handle = connp->conn_upper_handle;
+
+		/*
+		 * Set these to NULL first because closed() will free upper
+		 * structures.  Acquire conn_lock because an external caller
+		 * like conn_get_socket_info() will upcall if these are
+		 * non-NULL.
+		 */
+		mutex_enter(&connp->conn_lock);
+		connp->conn_upper_handle = NULL;
+		connp->conn_upcalls = NULL;
+		mutex_exit(&connp->conn_lock);
 		if (IPCL_IS_NONSTR(connp)) {
-			(*connp->conn_upcalls->su_closed)(
-			    connp->conn_upper_handle);
+			ASSERT(upcalls != NULL);
+			ASSERT(upcalls->su_closed != NULL);
+			ASSERT(handle != NULL);
+			upcalls->su_closed(handle);
 			tcp->tcp_detached = B_TRUE;
 		}
-		connp->conn_upper_handle = NULL;
-		connp->conn_upcalls = NULL;
 	}
 }
 
--- old/usr/src/uts/common/inet/tcp/tcp_output.c	Thu Aug 27 14:38:33 2020
+++ new/usr/src/uts/common/inet/tcp/tcp_output.c	Thu Aug 27 14:38:33 2020
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2014, 2017 by Delphix. All rights reserved.
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
  */
 
 /* This file contains all TCP output processing functions. */
@@ -1677,11 +1677,23 @@
 
 		/* non-STREAM socket, release the upper handle */
 		if (IPCL_IS_NONSTR(connp)) {
-			ASSERT(connp->conn_upper_handle != NULL);
-			(*connp->conn_upcalls->su_closed)
-			    (connp->conn_upper_handle);
+			sock_upcalls_t *upcalls = connp->conn_upcalls;
+			sock_upper_handle_t handle = connp->conn_upper_handle;
+
+			ASSERT(upcalls != NULL);
+			ASSERT(upcalls->su_closed != NULL);
+			ASSERT(handle != NULL);
+			/*
+			 * Set these to NULL first because closed() will free
+			 * upper structures.  Acquire conn_lock because an
+			 * external caller like conn_get_socket_info() will
+			 * upcall if these are non-NULL.
+			 */
+			mutex_enter(&connp->conn_lock);
 			connp->conn_upper_handle = NULL;
 			connp->conn_upcalls = NULL;
+			mutex_exit(&connp->conn_lock);
+			upcalls->su_closed(handle);
 		}
 	}
 
--- old/usr/src/uts/common/sys/socket_proto.h	Thu Aug 27 14:38:33 2020
+++ new/usr/src/uts/common/sys/socket_proto.h	Thu Aug 27 14:38:33 2020
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
+ * Copyright 2020 Joyent, Inc.
  */
 
 #ifndef _SYS_SOCKET_PROTO_H_
@@ -202,7 +203,16 @@
 	void	(*su_signal_oob)(sock_upper_handle_t, ssize_t);
 	void	(*su_zcopy_notify)(sock_upper_handle_t);
 	void	(*su_set_error)(sock_upper_handle_t, int);
+	/*
+	 * NOTE: This function frees upper handle items. Caller cannot
+	 * rely on them after this upcall.
+	 */
 	void	(*su_closed)(sock_upper_handle_t);
+	/*
+	 * NOTE: This function MUST be implemented without using lower-level
+	 * downcalls or accesses. This allows callers to ensure su_closed()
+	 * upcalls can happen indepdently or concurrently.
+	 */
 	vnode_t *(*su_get_vnode)(sock_upper_handle_t);
 };
 
