SUNRPC: Do not dereference non-socket transports in sysfs

Do not cast the struct xprt to a sock_xprt unless we know it is a UDP or
TCP transport. Otherwise the call to lock the mutex will scribble over
whatever structure is actually there. This has been seen to cause hard
system lockups when the underlying transport was RDMA.

Fixes: b49ea673e1 ("SUNRPC: lock against ->sock changing during sysfs read")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
Trond Myklebust
2022-03-25 10:37:31 -04:00
parent 1d15d121cc
commit 421ab1be43
4 changed files with 54 additions and 31 deletions

View File

@@ -97,7 +97,7 @@ static ssize_t rpc_sysfs_xprt_dstaddr_show(struct kobject *kobj,
return 0;
ret = sprintf(buf, "%s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
xprt_put(xprt);
return ret + 1;
return ret;
}
static ssize_t rpc_sysfs_xprt_srcaddr_show(struct kobject *kobj,
@@ -105,33 +105,31 @@ static ssize_t rpc_sysfs_xprt_srcaddr_show(struct kobject *kobj,
char *buf)
{
struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);
struct sockaddr_storage saddr;
struct sock_xprt *sock;
ssize_t ret = -1;
size_t buflen = PAGE_SIZE;
ssize_t ret = -ENOTSOCK;
if (!xprt || !xprt_connected(xprt)) {
xprt_put(xprt);
return -ENOTCONN;
ret = -ENOTCONN;
} else if (xprt->ops->get_srcaddr) {
ret = xprt->ops->get_srcaddr(xprt, buf, buflen);
if (ret > 0) {
if (ret < buflen - 1) {
buf[ret] = '\n';
ret++;
buf[ret] = '\0';
}
}
}
sock = container_of(xprt, struct sock_xprt, xprt);
mutex_lock(&sock->recv_mutex);
if (sock->sock == NULL ||
kernel_getsockname(sock->sock, (struct sockaddr *)&saddr) < 0)
goto out;
ret = sprintf(buf, "%pISc\n", &saddr);
out:
mutex_unlock(&sock->recv_mutex);
xprt_put(xprt);
return ret + 1;
return ret;
}
static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
struct kobj_attribute *attr, char *buf)
{
struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj);
unsigned short srcport = 0;
size_t buflen = PAGE_SIZE;
ssize_t ret;
if (!xprt || !xprt_connected(xprt)) {
@@ -139,7 +137,11 @@ static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,
return -ENOTCONN;
}
ret = sprintf(buf, "last_used=%lu\ncur_cong=%lu\ncong_win=%lu\n"
if (xprt->ops->get_srcport)
srcport = xprt->ops->get_srcport(xprt);
ret = snprintf(buf, buflen,
"last_used=%lu\ncur_cong=%lu\ncong_win=%lu\n"
"max_num_slots=%u\nmin_num_slots=%u\nnum_reqs=%u\n"
"binding_q_len=%u\nsending_q_len=%u\npending_q_len=%u\n"
"backlog_q_len=%u\nmain_xprt=%d\nsrc_port=%u\n"
@@ -147,14 +149,11 @@ static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,
xprt->last_used, xprt->cong, xprt->cwnd, xprt->max_reqs,
xprt->min_reqs, xprt->num_reqs, xprt->binding.qlen,
xprt->sending.qlen, xprt->pending.qlen,
xprt->backlog.qlen, xprt->main,
(xprt->xprt_class->ident == XPRT_TRANSPORT_TCP) ?
get_srcport(xprt) : 0,
xprt->backlog.qlen, xprt->main, srcport,
atomic_long_read(&xprt->queuelen),
(xprt->xprt_class->ident == XPRT_TRANSPORT_TCP) ?
xprt->address_strings[RPC_DISPLAY_PORT] : "0");
xprt->address_strings[RPC_DISPLAY_PORT]);
xprt_put(xprt);
return ret + 1;
return ret;
}
static ssize_t rpc_sysfs_xprt_state_show(struct kobject *kobj,
@@ -201,7 +200,7 @@ static ssize_t rpc_sysfs_xprt_state_show(struct kobject *kobj,
}
xprt_put(xprt);
return ret + 1;
return ret;
}
static ssize_t rpc_sysfs_xprt_switch_info_show(struct kobject *kobj,
@@ -220,7 +219,7 @@ static ssize_t rpc_sysfs_xprt_switch_info_show(struct kobject *kobj,
xprt_switch->xps_nunique_destaddr_xprts,
atomic_long_read(&xprt_switch->xps_queuelen));
xprt_switch_put(xprt_switch);
return ret + 1;
return ret;
}
static ssize_t rpc_sysfs_xprt_dstaddr_store(struct kobject *kobj,