Print this page
14685 sotpi ops need to be wary of null v_stream
@@ -21,10 +21,11 @@
/*
* 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>
#include <sys/t_lock.h>
#include <sys/param.h>
@@ -259,10 +260,29 @@
sotpi_poll, /* sop_poll */
sotpi_close, /* sop_close */
};
/*
+ * 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
* or by using the clone driver) i.e. a socket() call will always
* result in a new vnode being created.
@@ -1001,11 +1021,11 @@
VN_HOLD(rvp);
VN_RELE(vp);
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;
mutex_exit(&vp->v_lock);
@@ -4966,17 +4986,23 @@
int error = 0, res;
void *addr;
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)));
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,
MIN(*namelen, sti->sti_faddr_len));
*namelen = sti->sti_faddr_noxlate ? 0: sti->sti_faddr_len;
@@ -5037,11 +5063,11 @@
strbuf.len = 0;
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);
mutex_enter(&so->so_lock);
/*
@@ -5102,17 +5128,23 @@
int error = 0, res;
void *addr;
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)));
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
dprintso(so, 1, ("sotpi_getsockname (local): %s\n",
pr_addr(so->so_family, sti->sti_laddr_sa,
@@ -5162,11 +5194,11 @@
strbuf.len = 0;
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);
mutex_enter(&so->so_lock);
/*
@@ -5236,17 +5268,24 @@
t_uscalar_t len;
uint32_t value;
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,
pr_state(so->so_state, so->so_mode)));
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.
* Certain SOL_SOCKET options are returned directly whereas
* others only provide a default (fallback) value should
@@ -5387,11 +5426,11 @@
* the T_SVR4_OPTMGMT_REQ.
*/
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,
("got SO_SNDBUF %ld from q\n", lvalue));
}
@@ -5417,11 +5456,11 @@
* that the transport is actually using.
*/
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,
("got SO_RCVBUF %ld from q\n", lvalue));
} else if (flags & _SOGETSOCKOPT_XPG4_2) {
@@ -5503,11 +5542,11 @@
oh.len = maxlen;
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);
if (error) {
eprintsoline(so, error);