rxrpc: Fix apparent leak of rxrpc_local objects
authorDavid Howells <dhowells@redhat.com>
Fri, 30 Mar 2018 20:05:33 +0000 (21:05 +0100)
committerDavid Howells <dhowells@redhat.com>
Fri, 30 Mar 2018 20:05:33 +0000 (21:05 +0100)
rxrpc_local objects cannot be disposed of until all the connections that
point to them have been RCU'd as a connection object holds refcount on the
local endpoint it is communicating through.  Currently, this can cause an
assertion failure to occur when a network namespace is destroyed as there's
no check that the RCU destructors for the connections have been run before
we start trying to destroy local endpoints.

The kernel reports:

rxrpc: AF_RXRPC: Leaked local 0000000036a41bc1 {5}
------------[ cut here ]------------
kernel BUG at ../net/rxrpc/local_object.c:439!

Fix this by keeping a count of the live connections and waiting for it to
go to zero at the end of rxrpc_destroy_all_connections().

Fixes: dee46364ce6f ("rxrpc: Add RCU destruction for connections and calls")
Signed-off-by: David Howells <dhowells@redhat.com>
net/rxrpc/ar-internal.h
net/rxrpc/call_accept.c
net/rxrpc/conn_client.c
net/rxrpc/conn_object.c
net/rxrpc/conn_service.c
net/rxrpc/net_ns.c

index cc51d3eb0548cd4b43cbb9f9f5fd7d0d84dfc537..d40d54b785676aa75bac22328fbb410c0ea9fe82 100644 (file)
@@ -77,6 +77,7 @@ struct rxrpc_net {
        rwlock_t                call_lock;      /* Lock for ->calls */
        atomic_t                nr_calls;       /* Count of allocated calls */
 
+       atomic_t                nr_conns;
        struct list_head        conn_proc_list; /* List of conns in this namespace for proc */
        struct list_head        service_conns;  /* Service conns in this namespace */
        rwlock_t                conn_lock;      /* Lock for ->conn_proc_list, ->service_conns */
index 5a9b1d91612462cf6f0bb8470897db305f7130ac..f67017dcb25e00cc9e595d3e0c1824d0b15d63ae 100644 (file)
@@ -219,6 +219,8 @@ void rxrpc_discard_prealloc(struct rxrpc_sock *rx)
                list_del(&conn->proc_link);
                write_unlock(&rxnet->conn_lock);
                kfree(conn);
+               if (atomic_dec_and_test(&rxnet->nr_conns))
+                       wake_up_atomic_t(&rxnet->nr_conns);
                tail = (tail + 1) & (size - 1);
        }
 
index 041da40dbf936d20f37b61cc12fc6803a4125524..5736f643c51646b603cbad57330930bba6712ff1 100644 (file)
@@ -207,6 +207,7 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
        if (ret < 0)
                goto error_2;
 
+       atomic_inc(&rxnet->nr_conns);
        write_lock(&rxnet->conn_lock);
        list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
        write_unlock(&rxnet->conn_lock);
index bfc46fd69a621d6d25e5548223578f642e2e0347..0950ee3d26f56918b8dbb1ef3b1925c8755776bf 100644 (file)
@@ -365,6 +365,9 @@ static void rxrpc_destroy_connection(struct rcu_head *rcu)
        key_put(conn->params.key);
        key_put(conn->server_key);
        rxrpc_put_peer(conn->params.peer);
+
+       if (atomic_dec_and_test(&conn->params.local->rxnet->nr_conns))
+               wake_up_atomic_t(&conn->params.local->rxnet->nr_conns);
        rxrpc_put_local(conn->params.local);
 
        kfree(conn);
@@ -458,6 +461,7 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
 
        _enter("");
 
+       atomic_dec(&rxnet->nr_conns);
        rxrpc_destroy_all_client_connections(rxnet);
 
        del_timer_sync(&rxnet->service_conn_reap_timer);
@@ -475,5 +479,9 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
 
        ASSERT(list_empty(&rxnet->conn_proc_list));
 
+       /* We need to wait for the connections to be destroyed by RCU as they
+        * pin things that we still need to get rid of.
+        */
+       wait_on_atomic_t(&rxnet->nr_conns, atomic_t_wait, TASK_UNINTERRUPTIBLE);
        _leave("");
 }
index f6fcdb3130a19a9bcb74b2d7dd5af7886ed8912d..80773a50c755168773c758cb5c2b5b86c117cf02 100644 (file)
@@ -132,6 +132,7 @@ struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxn
                conn->state = RXRPC_CONN_SERVICE_PREALLOC;
                atomic_set(&conn->usage, 2);
 
+               atomic_inc(&rxnet->nr_conns);
                write_lock(&rxnet->conn_lock);
                list_add_tail(&conn->link, &rxnet->service_conns);
                list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
index 101019b0be34bf7c25955b63d32713d7ac0f51fd..fa9ce60e7bfa598029e6de402111f94281071381 100644 (file)
@@ -57,6 +57,7 @@ static __net_init int rxrpc_init_net(struct net *net)
        rwlock_init(&rxnet->call_lock);
        atomic_set(&rxnet->nr_calls, 1);
 
+       atomic_set(&rxnet->nr_conns, 1);
        INIT_LIST_HEAD(&rxnet->conn_proc_list);
        INIT_LIST_HEAD(&rxnet->service_conns);
        rwlock_init(&rxnet->conn_lock);