--- old/usr/src/uts/common/fs/sockfs/socktpi.c	Tue Jun 21 13:31:34 2022
+++ new/usr/src/uts/common/fs/sockfs/socktpi.c	Tue Jun 21 13:31:34 2022
@@ -23,6 +23,7 @@
  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2015, Joyent, Inc.
  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2022 MNX Cloud, Inc.
  */
 
 #include <sys/types.h>
@@ -261,6 +262,25 @@
 };
 
 /*
+ * Post-close reality check for NULL v_stream...
+ *
+ * Kernel callers (e.g. in procfs) may attempt socket operations, after
+ * holding the vnode, after it has been closed.  For TPI sockets, post-close
+ * operations will have a NULL v_stream (which all functions here assume
+ * or even ASSERT() is non-NULL).  See sotpi_close for where we wipe it out.
+ *
+ * If we are in a state where we lost a race to close(), we need to stop ASAP,
+ * and return the acceptable-as-an-errno EBADF.  Because cleanup may be
+ * required, this macro only checks the v_stream.
+ *
+ * Checking should only be relevant for in-kernel other-thread inspectors.
+ * Userland ones (i.e. same process that opened the socktpi socket) SHOULD be
+ * protected by higher-level mechanisms. The only in-kernel inspector in the
+ * source base is procfs, which only accesses get{sockname,peername,sockopt}().
+ */
+#define	SOTPI_VN_NOSTREAM(vn) ((vn)->v_stream == NULL)
+
+/*
  * Return a TPI socket vnode.
  *
  * Note that sockets assume that the driver will clone (either itself
@@ -1003,7 +1023,7 @@
 				vp = rvp;
 			}
 
-			ASSERT(SOTOV(so)->v_stream);
+			ASSERT(SOTOV(so)->v_stream != NULL);
 			mutex_enter(&vp->v_lock);
 			vp->v_stream = SOTOV(so)->v_stream;
 			sti->sti_ux_bound_vp = vp;
@@ -4968,6 +4988,7 @@
 	t_uscalar_t	addrlen;
 	k_sigset_t	smask;
 	sotpi_info_t	*sti = SOTOTPI(so);
+	vnode_t		*vn;
 
 	dprintso(so, 1, ("sotpi_getpeername(%p) %s\n",
 	    (void *)so, pr_state(so->so_state, so->so_mode)));
@@ -4975,6 +4996,11 @@
 	ASSERT(*namelen > 0);
 	mutex_enter(&so->so_lock);
 	so_lock_single(so);	/* Set SOLOCKED */
+	vn = SOTOV(so);
+	if (SOTPI_VN_NOSTREAM(vn)) {
+		error = EBADF;
+		goto done;
+	}
 
 	if (accept) {
 		bcopy(sti->sti_faddr_sa, name,
@@ -5039,7 +5065,7 @@
 	sigintr(&smask, 0);
 	res = 0;
 	ASSERT(cr);
-	error = strioctl(SOTOV(so), TI_GETPEERNAME, (intptr_t)&strbuf,
+	error = strioctl(vn, TI_GETPEERNAME, (intptr_t)&strbuf,
 	    0, K_TO_K, cr, &res);
 	sigunintr(&smask);
 
@@ -5104,6 +5130,7 @@
 	t_uscalar_t	addrlen;
 	k_sigset_t	smask;
 	sotpi_info_t	*sti = SOTOTPI(so);
+	vnode_t		*vn;
 
 	dprintso(so, 1, ("sotpi_getsockname(%p) %s\n",
 	    (void *)so, pr_state(so->so_state, so->so_mode)));
@@ -5111,6 +5138,11 @@
 	ASSERT(*namelen > 0);
 	mutex_enter(&so->so_lock);
 	so_lock_single(so);	/* Set SOLOCKED */
+	vn = SOTOV(so);
+	if (SOTPI_VN_NOSTREAM(vn)) {
+		error = EBADF;
+		goto done;
+	}
 
 #ifdef DEBUG
 
@@ -5164,7 +5196,7 @@
 	sigintr(&smask, 0);
 	res = 0;
 	ASSERT(cr);
-	error = strioctl(SOTOV(so), TI_GETMYNAME, (intptr_t)&strbuf,
+	error = strioctl(vn, TI_GETMYNAME, (intptr_t)&strbuf,
 	    0, K_TO_K, cr, &res);
 	sigunintr(&smask);
 
@@ -5238,6 +5270,7 @@
 	struct timeval		tmo_val; /* used for SO_RCVTIMEO, SO_SNDTIMEO */
 	struct timeval32	tmo_val32;
 	struct so_snd_bufinfo	snd_bufinfo;	/* used for zero copy */
+	vnode_t			*vn;
 
 	dprintso(so, 1, ("sotpi_getsockopt(%p, 0x%x, 0x%x, %p, %p) %s\n",
 	    (void *)so, level, option_name, optval, (void *)optlenp,
@@ -5245,6 +5278,12 @@
 
 	mutex_enter(&so->so_lock);
 	so_lock_single(so);	/* Set SOLOCKED */
+	vn = SOTOV(so);
+	if (SOTPI_VN_NOSTREAM(vn)) {
+		error = EBADF;
+		eprintsoline(so, error);
+		goto done2;
+	}
 
 	/*
 	 * Check for SOL_SOCKET options.
@@ -5389,7 +5428,7 @@
 			lvalue = so->so_sndbuf;
 			if (lvalue == 0) {
 				mutex_exit(&so->so_lock);
-				(void) strqget(strvp2wq(SOTOV(so))->q_next,
+				(void) strqget(strvp2wq(vn)->q_next,
 				    QHIWAT, 0, &lvalue);
 				mutex_enter(&so->so_lock);
 				dprintso(so, 1,
@@ -5419,7 +5458,7 @@
 			lvalue = so->so_rcvbuf;
 			if (lvalue == 0) {
 				mutex_exit(&so->so_lock);
-				(void) strqget(RD(strvp2wq(SOTOV(so))),
+				(void) strqget(RD(strvp2wq(vn)),
 				    QHIWAT, 0, &lvalue);
 				mutex_enter(&so->so_lock);
 				dprintso(so, 1,
@@ -5505,7 +5544,7 @@
 	mp = soallocproto3(&optmgmt_req, sizeof (optmgmt_req),
 	    &oh, sizeof (oh), NULL, maxlen, 0, _ALLOC_SLEEP, cr);
 	/* Let option management work in the presence of data flow control */
-	error = kstrputmsg(SOTOV(so), mp, NULL, 0, 0,
+	error = kstrputmsg(vn, mp, NULL, 0, 0,
 	    MSG_BAND|MSG_HOLDSIG|MSG_IGNERROR|MSG_IGNFLOW, 0);
 	mp = NULL;
 	mutex_enter(&so->so_lock);
