Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31f5f9a1 authored by David Howells's avatar David Howells
Browse files

rxrpc: Fix apparent leak of rxrpc_local objects



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: dee46364 ("rxrpc: Add RCU destruction for connections and calls")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 09d2bf59
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 */
+2 −0
Original line number Diff line number Diff line
@@ -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);
	}

+1 −0
Original line number Diff line number Diff line
@@ -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);
+8 −0
Original line number Diff line number Diff line
@@ -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("");
}
+1 −0
Original line number Diff line number Diff line
@@ -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);
Loading