Skip to content

Commit

Permalink
SUNRPC: Do not dereference non-socket transports in sysfs
Browse files Browse the repository at this point in the history
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: b49ea67 ("SUNRPC: lock against ->sock changing during sysfs read")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
  • Loading branch information
Trond Myklebust committed Mar 25, 2022
1 parent 1d15d12 commit 421ab1b
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 31 deletions.
3 changes: 3 additions & 0 deletions include/linux/sunrpc/xprt.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ struct rpc_xprt_ops {
void (*rpcbind)(struct rpc_task *task);
void (*set_port)(struct rpc_xprt *xprt, unsigned short port);
void (*connect)(struct rpc_xprt *xprt, struct rpc_task *task);
int (*get_srcaddr)(struct rpc_xprt *xprt, char *buf,
size_t buflen);
unsigned short (*get_srcport)(struct rpc_xprt *xprt);
int (*buf_alloc)(struct rpc_task *task);
void (*buf_free)(struct rpc_task *task);
void (*prepare_request)(struct rpc_rqst *req);
Expand Down
1 change: 0 additions & 1 deletion include/linux/sunrpc/xprtsock.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

int init_socket_xprt(void);
void cleanup_socket_xprt(void);
unsigned short get_srcport(struct rpc_xprt *);

#define RPC_MIN_RESVPORT (1U)
#define RPC_MAX_RESVPORT (65535U)
Expand Down
55 changes: 27 additions & 28 deletions net/sunrpc/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,64 +97,63 @@ 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,
struct kobj_attribute *attr,
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)) {
xprt_put(xprt);
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"
"tasks_queuelen=%ld\ndst_port=%s\n",
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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
26 changes: 24 additions & 2 deletions net/sunrpc/xprtsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ static int xs_get_srcport(struct sock_xprt *transport)
return port;
}

unsigned short get_srcport(struct rpc_xprt *xprt)
static unsigned short xs_sock_srcport(struct rpc_xprt *xprt)
{
struct sock_xprt *sock = container_of(xprt, struct sock_xprt, xprt);
unsigned short ret = 0;
Expand All @@ -1648,7 +1648,25 @@ unsigned short get_srcport(struct rpc_xprt *xprt)
mutex_unlock(&sock->recv_mutex);
return ret;
}
EXPORT_SYMBOL(get_srcport);

static int xs_sock_srcaddr(struct rpc_xprt *xprt, char *buf, size_t buflen)
{
struct sock_xprt *sock = container_of(xprt, struct sock_xprt, xprt);
union {
struct sockaddr sa;
struct sockaddr_storage st;
} saddr;
int ret = -ENOTCONN;

mutex_lock(&sock->recv_mutex);
if (sock->sock) {
ret = kernel_getsockname(sock->sock, &saddr.sa);
if (ret >= 0)
ret = snprintf(buf, buflen, "%pISc", &saddr.sa);
}
mutex_unlock(&sock->recv_mutex);
return ret;
}

static unsigned short xs_next_srcport(struct sock_xprt *transport, unsigned short port)
{
Expand Down Expand Up @@ -2622,6 +2640,8 @@ static const struct rpc_xprt_ops xs_udp_ops = {
.rpcbind = rpcb_getport_async,
.set_port = xs_set_port,
.connect = xs_connect,
.get_srcaddr = xs_sock_srcaddr,
.get_srcport = xs_sock_srcport,
.buf_alloc = rpc_malloc,
.buf_free = rpc_free,
.send_request = xs_udp_send_request,
Expand All @@ -2644,6 +2664,8 @@ static const struct rpc_xprt_ops xs_tcp_ops = {
.rpcbind = rpcb_getport_async,
.set_port = xs_set_port,
.connect = xs_connect,
.get_srcaddr = xs_sock_srcaddr,
.get_srcport = xs_sock_srcport,
.buf_alloc = rpc_malloc,
.buf_free = rpc_free,
.prepare_request = xs_stream_prepare_request,
Expand Down

0 comments on commit 421ab1b

Please sign in to comment.