]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
RDS tcp loopback connection can hang
authorRao Shoaib <rao.shoaib@oracle.com>
Fri, 21 May 2021 18:08:06 +0000 (11:08 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 16 Jun 2021 09:54:57 +0000 (11:54 +0200)
[ Upstream commit aced3ce57cd37b5ca332bcacd370d01f5a8c5371 ]

When TCP is used as transport and a program on the
system connects to RDS port 16385, connection is
accepted but denied per the rules of RDS. However,
RDS connections object is left in the list. Next
loopback connection will select that connection
object as it is at the head of list. The connection
attempt will hang as the connection object is set
to connect over TCP which is not allowed

The issue can be reproduced easily, use rds-ping
to ping a local IP address. After that use any
program like ncat to connect to the same IP
address and port 16385. This will hang so ctrl-c out.
Now try rds-ping, it will hang.

To fix the issue this patch adds checks to disallow
the connection object creation and destroys the
connection object.

Signed-off-by: Rao Shoaib <rao.shoaib@oracle.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/rds/connection.c
net/rds/tcp.c
net/rds/tcp.h
net/rds/tcp_listen.c

index 3bd2f4a5a30d9eccc83f3242db0a59031d5ac5a3..ac3300b204a6f427c4fa7f7e6b0c1a23922b1611 100644 (file)
@@ -237,12 +237,23 @@ static struct rds_connection *__rds_conn_create(struct net *net,
        if (loop_trans) {
                rds_trans_put(loop_trans);
                conn->c_loopback = 1;
-               if (is_outgoing && trans->t_prefer_loopback) {
-                       /* "outgoing" connection - and the transport
-                        * says it wants the connection handled by the
-                        * loopback transport. This is what TCP does.
-                        */
-                       trans = &rds_loop_transport;
+               if (trans->t_prefer_loopback) {
+                       if (likely(is_outgoing)) {
+                               /* "outgoing" connection to local address.
+                                * Protocol says it wants the connection
+                                * handled by the loopback transport.
+                                * This is what TCP does.
+                                */
+                               trans = &rds_loop_transport;
+                       } else {
+                               /* No transport currently in use
+                                * should end up here, but if it
+                                * does, reset/destroy the connection.
+                                */
+                               kmem_cache_free(rds_conn_slab, conn);
+                               conn = ERR_PTR(-EOPNOTSUPP);
+                               goto out;
+                       }
                }
        }
 
index 18bb522df2823905b754311fe1eab882e4bcb857..d0bce439198fc59b5fcc8aaff3119369d5f063cf 100644 (file)
@@ -322,8 +322,8 @@ out:
 }
 #endif
 
-static int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
-                              __u32 scope_id)
+int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+                       __u32 scope_id)
 {
        struct net_device *dev = NULL;
 #if IS_ENABLED(CONFIG_IPV6)
index 3c69361d21c730c4680033382049f4a0b457a727..4620549ecbebee4c96c4e631407e23b6d694d039 100644 (file)
@@ -60,7 +60,8 @@ u32 rds_tcp_snd_una(struct rds_tcp_connection *tc);
 u64 rds_tcp_map_seq(struct rds_tcp_connection *tc, u32 seq);
 extern struct rds_transport rds_tcp_transport;
 void rds_tcp_accept_work(struct sock *sk);
-
+int rds_tcp_laddr_check(struct net *net, const struct in6_addr *addr,
+                       __u32 scope_id);
 /* tcp_connect.c */
 int rds_tcp_conn_path_connect(struct rds_conn_path *cp);
 void rds_tcp_conn_path_shutdown(struct rds_conn_path *conn);
index c12203f646da92e439dc17a35c6c31be8a56c4c4..0d095d3f5feecfdfddb9e58574a548ec4936c193 100644 (file)
@@ -198,6 +198,12 @@ int rds_tcp_accept_one(struct socket *sock)
        }
 #endif
 
+       if (!rds_tcp_laddr_check(sock_net(sock->sk), peer_addr, dev_if)) {
+               /* local address connection is only allowed via loopback */
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+
        conn = rds_conn_create(sock_net(sock->sk),
                               my_addr, peer_addr,
                               &rds_tcp_transport, GFP_KERNEL, dev_if);