]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
SUNRPC: Do not dereference non-socket transports in sysfs
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 25 Mar 2022 14:37:31 +0000 (10:37 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 8 Apr 2022 11:57:17 +0000 (13:57 +0200)
commit 421ab1be43bd015ffe744f4ea25df4f19d1ce6fe upstream.

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: b49ea673e119 ("SUNRPC: lock against ->sock changing during sysfs read")
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/sunrpc/xprt.h
include/linux/sunrpc/xprtsock.h
net/sunrpc/sysfs.c
net/sunrpc/xprtsock.c

index 955ea4d7af0b2fea1300a46fad963df35f25810c..eef5e87c03b43f239a02f1333abd48677055d4f1 100644 (file)
@@ -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);
index 8c2a712cb24202824aa188dfb3283c42890bfeb4..3c1423ee74b4eb18733d171a703ba6cef3e40984 100644 (file)
@@ -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)
index 05c758da6a92a44e064431c927b97a05504794e6..9d8a7d9f3e412983e5c2224752ca1df971651b76 100644 (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,
index 0f39e08ee580e8f383b8a5fa6d7bd9ccc2cb0380..e9b2091c9981f5347677cda66465ed59dca59255 100644 (file)
@@ -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;
@@ -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)
 {
@@ -2621,6 +2639,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,
@@ -2643,6 +2663,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,