]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
rds: drop incoming messages that cross network namespace boundaries
authorAldo Ariel Panzardo <qwe.aldo@gmail.com>
Wed, 8 Jul 2026 02:43:14 +0000 (19:43 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 21 Jul 2026 09:12:51 +0000 (11:12 +0200)
rds_find_bound() looks up the destination socket using a global
rhashtable keyed solely on (addr, port, scope_id).  Network namespaces
are not part of the key, so a sender in netns A can deliver an incoming
message (inc) to a socket that lives in a different netns B.

When this happens, inc->i_conn points to an rds_connection whose c_net
is netns A, but the receiving rs lives in netns B.  Once the child
process that created netns A exits, cleanup_net() calls
rds_loop_exit_net() -> rds_loop_kill_conns() -> rds_conn_destroy(),
freeing that connection.  If the survivor socket in netns B still holds
the inc, any subsequent dereference of inc->i_conn is a use-after-free.

There are two dangerous sites in rds_clear_recv_queue():
  1. inc->i_conn->c_lcong (offset 88 of freed rds_connection, size 200)
     read via rds_recv_rcvbuf_delta() -- confirmed by KASAN.
  2. inc->i_conn->c_trans->inc_free(inc) (function pointer at offset 80)
     called via rds_inc_put() when the inc refcount reaches zero -- same
     race window, potential call-through-freed-object primitive.

The bug is reachable from unprivileged user namespaces
(CLONE_NEWUSER + CLONE_NEWNET), available since Linux 3.8.

Fix this by rejecting the delivery in rds_recv_incoming() when the
socket returned by rds_find_bound() belongs to a different network
namespace than the connection that carried the message.  Use the
existing rds_conn_net() / sock_net() helpers and net_eq() for the
comparison.

Fixes: c809195f5523 ("rds: clean up loopback rds_connections on netns deletion")
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
Reviewed-by: Allison Henderson <achender@kernel.org>
Tested-by: Allison Henderson <achender@kernel.org>
Signed-off-by: Allison Henderson <achender@kernel.org>
Link: https://patch.msgid.link/20260708024314.601139-1-achender@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/rds/recv.c

index 4b3f9e4a8bfda57714cee4222a876e72a2c46c57..cf3884d8793195420d57d5526cc3595746546b56 100644 (file)
@@ -399,6 +399,21 @@ void rds_recv_incoming(struct rds_connection *conn, struct in6_addr *saddr,
                goto out;
        }
 
+       /*
+        * rds_find_bound() uses a global (netns-agnostic) hash table.
+        * An RDS connection created in netns A can match a socket bound
+        * in the init netns, delivering inc cross-netns with inc->i_conn
+        * pointing into netns A.  When cleanup_net() then frees that conn,
+        * any subsequent dereference of inc->i_conn is a use-after-free.
+        * Drop the inc if the receiving socket lives in a different netns.
+        */
+       if (!net_eq(sock_net(rds_rs_to_sk(rs)), rds_conn_net(conn))) {
+               rds_stats_inc(s_recv_drop_no_sock);
+               rds_sock_put(rs);
+               rs = NULL;
+               goto out;
+       }
+
        /* Process extension headers */
        rds_recv_incoming_exthdrs(inc, rs);