Commit 17226f12 authored by David Howells's avatar David Howells

rxrpc: Fix leak of rxrpc_peer objects

When a new client call is requested, an rxrpc_conn_parameters struct object
is passed in with a bunch of parameters set, such as the local endpoint to
use.  A pointer to the target peer record is also placed in there by
rxrpc_get_client_conn() - and this is removed if and only if a new
connection object is allocated.  Thus it leaks if a new connection object
isn't allocated.

Fix this by putting any peer object attached to the rxrpc_conn_parameters
object in the function that allocated it.

Fixes: 19ffa01c ("rxrpc: Use structs to hold connection params and protocol info")
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 1159d4b4
...@@ -324,6 +324,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, ...@@ -324,6 +324,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
mutex_unlock(&call->user_mutex); mutex_unlock(&call->user_mutex);
} }
rxrpc_put_peer(cp.peer);
_leave(" = %p", call); _leave(" = %p", call);
return call; return call;
} }
...@@ -447,6 +448,7 @@ int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call, ...@@ -447,6 +448,7 @@ int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call,
ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL); ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL);
mutex_unlock(&call->user_mutex); mutex_unlock(&call->user_mutex);
rxrpc_put_peer(cp.peer);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
......
...@@ -1041,6 +1041,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *, ...@@ -1041,6 +1041,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *,
struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t); struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t);
struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *, struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *,
struct rxrpc_peer *); struct rxrpc_peer *);
void rxrpc_destroy_all_peers(struct rxrpc_net *);
struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *); struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *);
struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *); struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *);
void rxrpc_put_peer(struct rxrpc_peer *); void rxrpc_put_peer(struct rxrpc_peer *);
......
...@@ -118,6 +118,7 @@ static __net_exit void rxrpc_exit_net(struct net *net) ...@@ -118,6 +118,7 @@ static __net_exit void rxrpc_exit_net(struct net *net)
cancel_work_sync(&rxnet->peer_keepalive_work); cancel_work_sync(&rxnet->peer_keepalive_work);
rxrpc_destroy_all_calls(rxnet); rxrpc_destroy_all_calls(rxnet);
rxrpc_destroy_all_connections(rxnet); rxrpc_destroy_all_connections(rxnet);
rxrpc_destroy_all_peers(rxnet);
rxrpc_destroy_all_locals(rxnet); rxrpc_destroy_all_locals(rxnet);
proc_remove(rxnet->proc_net); proc_remove(rxnet->proc_net);
} }
......
...@@ -463,6 +463,27 @@ void rxrpc_put_peer(struct rxrpc_peer *peer) ...@@ -463,6 +463,27 @@ void rxrpc_put_peer(struct rxrpc_peer *peer)
} }
} }
/*
* Make sure all peer records have been discarded.
*/
void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet)
{
struct rxrpc_peer *peer;
int i;
for (i = 0; i < HASH_SIZE(rxnet->peer_hash); i++) {
if (hlist_empty(&rxnet->peer_hash[i]))
continue;
hlist_for_each_entry(peer, &rxnet->peer_hash[i], hash_link) {
pr_err("Leaked peer %u {%u} %pISp\n",
peer->debug_id,
atomic_read(&peer->usage),
&peer->srx.transport);
}
}
}
/** /**
* rxrpc_kernel_get_peer - Get the peer address of a call * rxrpc_kernel_get_peer - Get the peer address of a call
* @sock: The socket on which the call is in progress. * @sock: The socket on which the call is in progress.
......
...@@ -586,6 +586,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, ...@@ -586,6 +586,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
atomic_inc_return(&rxrpc_debug_id)); atomic_inc_return(&rxrpc_debug_id));
/* The socket is now unlocked */ /* The socket is now unlocked */
rxrpc_put_peer(cp.peer);
_leave(" = %p\n", call); _leave(" = %p\n", call);
return call; return call;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment