]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
rxrpc: Allow list of in-use local UDP endpoints to be viewed in /proc
authorDavid Howells <dhowells@redhat.com>
Sat, 21 May 2022 07:45:15 +0000 (08:45 +0100)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 14 Dec 2022 13:00:26 +0000 (14:00 +0100)
[ Upstream commit 33912c2639ad76660988c8ca97e4d18fca89b668 ]

Allow the list of in-use local UDP endpoints in the current network
namespace to be viewed in /proc.

To aid with this, the endpoint list is converted to an hlist and RCU-safe
manipulation is used so that the list can be read with only the RCU
read lock held.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
Signed-off-by: David S. Miller <davem@davemloft.net>
Stable-dep-of: 3bcd6c7eaa53 ("rxrpc: Fix race between conn bundle lookup and bundle removal [ZDI-CAN-15975]")
Signed-off-by: Sasha Levin <sashal@kernel.org>
(cherry picked from commit 3c33e41fa5b399df6f253fd03bf11003308005fe)
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
net/rxrpc/ar-internal.h
net/rxrpc/local_object.c
net/rxrpc/net_ns.c
net/rxrpc/proc.c

index f2e3fb77a02d383deebe7b5dc1ba7f47f8935c14..38dd63169aacff23d852157bfbaf376a546ac089 100644 (file)
@@ -88,7 +88,7 @@ struct rxrpc_net {
        struct work_struct      client_conn_reaper;
        struct timer_list       client_conn_reap_timer;
 
-       struct list_head        local_endpoints;
+       struct hlist_head       local_endpoints;
        struct mutex            local_mutex;    /* Lock for ->local_endpoints */
 
        DECLARE_HASHTABLE       (peer_hash, 10);
@@ -281,7 +281,7 @@ struct rxrpc_local {
        atomic_t                active_users;   /* Number of users of the local endpoint */
        atomic_t                usage;          /* Number of references to the structure */
        struct rxrpc_net        *rxnet;         /* The network ns in which this resides */
-       struct list_head        link;
+       struct hlist_node       link;
        struct socket           *socket;        /* my UDP socket */
        struct work_struct      processor;
        struct rxrpc_sock __rcu *service;       /* Service(s) listening on this endpoint */
@@ -1016,6 +1016,7 @@ void rxrpc_put_peer_locked(struct rxrpc_peer *);
 extern const struct seq_operations rxrpc_call_seq_ops;
 extern const struct seq_operations rxrpc_connection_seq_ops;
 extern const struct seq_operations rxrpc_peer_seq_ops;
+extern const struct seq_operations rxrpc_local_seq_ops;
 
 /*
  * recvmsg.c
index 1d15940f61d7e44ab185eef23f1fc460584a5495..0cbbbbcf490e948d7fe30ad9043b2a2298f81312 100644 (file)
@@ -82,7 +82,7 @@ static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet,
                atomic_set(&local->usage, 1);
                atomic_set(&local->active_users, 1);
                local->rxnet = rxnet;
-               INIT_LIST_HEAD(&local->link);
+               INIT_HLIST_NODE(&local->link);
                INIT_WORK(&local->processor, rxrpc_local_processor);
                init_rwsem(&local->defrag_sem);
                skb_queue_head_init(&local->reject_queue);
@@ -181,7 +181,7 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
 {
        struct rxrpc_local *local;
        struct rxrpc_net *rxnet = rxrpc_net(net);
-       struct list_head *cursor;
+       struct hlist_node *cursor;
        const char *age;
        long diff;
        int ret;
@@ -191,16 +191,12 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
 
        mutex_lock(&rxnet->local_mutex);
 
-       for (cursor = rxnet->local_endpoints.next;
-            cursor != &rxnet->local_endpoints;
-            cursor = cursor->next) {
-               local = list_entry(cursor, struct rxrpc_local, link);
+       hlist_for_each(cursor, &rxnet->local_endpoints) {
+               local = hlist_entry(cursor, struct rxrpc_local, link);
 
                diff = rxrpc_local_cmp_key(local, srx);
-               if (diff < 0)
+               if (diff != 0)
                        continue;
-               if (diff > 0)
-                       break;
 
                /* Services aren't allowed to share transport sockets, so
                 * reject that here.  It is possible that the object is dying -
@@ -212,9 +208,10 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
                        goto addr_in_use;
                }
 
-               /* Found a match.  We replace a dying object.  Attempting to
-                * bind the transport socket may still fail if we're attempting
-                * to use a local address that the dying object is still using.
+               /* Found a match.  We want to replace a dying object.
+                * Attempting to bind the transport socket may still fail if
+                * we're attempting to use a local address that the dying
+                * object is still using.
                 */
                if (!rxrpc_use_local(local))
                        break;
@@ -231,10 +228,12 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
        if (ret < 0)
                goto sock_error;
 
-       if (cursor != &rxnet->local_endpoints)
-               list_replace_init(cursor, &local->link);
-       else
-               list_add_tail(&local->link, cursor);
+       if (cursor) {
+               hlist_replace_rcu(cursor, &local->link);
+               cursor->pprev = NULL;
+       } else {
+               hlist_add_head_rcu(&local->link, &rxnet->local_endpoints);
+       }
        age = "new";
 
 found:
@@ -375,7 +374,7 @@ static void rxrpc_local_destroyer(struct rxrpc_local *local)
        local->dead = true;
 
        mutex_lock(&rxnet->local_mutex);
-       list_del_init(&local->link);
+       hlist_del_init_rcu(&local->link);
        mutex_unlock(&rxnet->local_mutex);
 
        rxrpc_clean_up_local_conns(local);
@@ -462,9 +461,9 @@ void rxrpc_destroy_all_locals(struct rxrpc_net *rxnet)
 
        flush_workqueue(rxrpc_workqueue);
 
-       if (!list_empty(&rxnet->local_endpoints)) {
+       if (!hlist_empty(&rxnet->local_endpoints)) {
                mutex_lock(&rxnet->local_mutex);
-               list_for_each_entry(local, &rxnet->local_endpoints, link) {
+               hlist_for_each_entry(local, &rxnet->local_endpoints, link) {
                        pr_err("AF_RXRPC: Leaked local %p {%d}\n",
                               local, atomic_read(&local->usage));
                }
index e4d6d432515bc510dae62ae62c89af2a9bfcef15..bb4c25d6df64c68db0a6d16457d75263928980c0 100644 (file)
@@ -72,7 +72,7 @@ static __net_init int rxrpc_init_net(struct net *net)
        timer_setup(&rxnet->client_conn_reap_timer,
                    rxrpc_client_conn_reap_timeout, 0);
 
-       INIT_LIST_HEAD(&rxnet->local_endpoints);
+       INIT_HLIST_HEAD(&rxnet->local_endpoints);
        mutex_init(&rxnet->local_mutex);
 
        hash_init(rxnet->peer_hash);
@@ -98,6 +98,9 @@ static __net_init int rxrpc_init_net(struct net *net)
        proc_create_net("peers", 0444, rxnet->proc_net,
                        &rxrpc_peer_seq_ops,
                        sizeof(struct seq_net_private));
+       proc_create_net("locals", 0444, rxnet->proc_net,
+                       &rxrpc_local_seq_ops,
+                       sizeof(struct seq_net_private));
        return 0;
 
 err_proc:
index 5a67955cc00f658e040bf184152d1c5915db70c3..7d007a66eba593cee589f78586eabe8ad989ff81 100644 (file)
@@ -328,3 +328,72 @@ const struct seq_operations rxrpc_peer_seq_ops = {
        .stop   = rxrpc_peer_seq_stop,
        .show   = rxrpc_peer_seq_show,
 };
+
+/*
+ * Generate a list of extant virtual local endpoints in /proc/net/rxrpc/locals
+ */
+static int rxrpc_local_seq_show(struct seq_file *seq, void *v)
+{
+       struct rxrpc_local *local;
+       char lbuff[50];
+
+       if (v == SEQ_START_TOKEN) {
+               seq_puts(seq,
+                        "Proto Local                                          "
+                        " Use Act\n");
+               return 0;
+       }
+
+       local = hlist_entry(v, struct rxrpc_local, link);
+
+       sprintf(lbuff, "%pISpc", &local->srx.transport);
+
+       seq_printf(seq,
+                  "UDP   %-47.47s %3u %3u\n",
+                  lbuff,
+                  atomic_read(&local->usage),
+                  atomic_read(&local->active_users));
+
+       return 0;
+}
+
+static void *rxrpc_local_seq_start(struct seq_file *seq, loff_t *_pos)
+       __acquires(rcu)
+{
+       struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
+       unsigned int n;
+
+       rcu_read_lock();
+
+       if (*_pos >= UINT_MAX)
+               return NULL;
+
+       n = *_pos;
+       if (n == 0)
+               return SEQ_START_TOKEN;
+
+       return seq_hlist_start_rcu(&rxnet->local_endpoints, n - 1);
+}
+
+static void *rxrpc_local_seq_next(struct seq_file *seq, void *v, loff_t *_pos)
+{
+       struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
+
+       if (*_pos >= UINT_MAX)
+               return NULL;
+
+       return seq_hlist_next_rcu(v, &rxnet->local_endpoints, _pos);
+}
+
+static void rxrpc_local_seq_stop(struct seq_file *seq, void *v)
+       __releases(rcu)
+{
+       rcu_read_unlock();
+}
+
+const struct seq_operations rxrpc_local_seq_ops = {
+       .start  = rxrpc_local_seq_start,
+       .next   = rxrpc_local_seq_next,
+       .stop   = rxrpc_local_seq_stop,
+       .show   = rxrpc_local_seq_show,
+};