diff options
author | David Howells <dhowells@redhat.com> | 2018-03-30 16:05:44 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2018-03-30 16:05:44 -0400 |
commit | 17226f1240381812c3a4927dc9da2814fb71c8ac (patch) | |
tree | 49ef90fafb4ae55ff13e0c373a81bbc509718bd8 | |
parent | 1159d4b496f57d5b8ee27c8b90b9d01c332e2e11 (diff) |
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: 19ffa01c9c45 ("rxrpc: Use structs to hold connection params and protocol info")
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | net/rxrpc/af_rxrpc.c | 2 | ||||
-rw-r--r-- | net/rxrpc/ar-internal.h | 1 | ||||
-rw-r--r-- | net/rxrpc/net_ns.c | 1 | ||||
-rw-r--r-- | net/rxrpc/peer_object.c | 21 | ||||
-rw-r--r-- | net/rxrpc/sendmsg.c | 1 |
5 files changed, 26 insertions, 0 deletions
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 0b3026b8fa40..9a2c8e7c000e 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c | |||
@@ -324,6 +324,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, | |||
324 | mutex_unlock(&call->user_mutex); | 324 | mutex_unlock(&call->user_mutex); |
325 | } | 325 | } |
326 | 326 | ||
327 | rxrpc_put_peer(cp.peer); | ||
327 | _leave(" = %p", call); | 328 | _leave(" = %p", call); |
328 | return call; | 329 | return call; |
329 | } | 330 | } |
@@ -447,6 +448,7 @@ int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call, | |||
447 | ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL); | 448 | ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL); |
448 | 449 | ||
449 | mutex_unlock(&call->user_mutex); | 450 | mutex_unlock(&call->user_mutex); |
451 | rxrpc_put_peer(cp.peer); | ||
450 | _leave(" = %d", ret); | 452 | _leave(" = %d", ret); |
451 | return ret; | 453 | return ret; |
452 | } | 454 | } |
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index c46583bc255d..90d7079e0aa9 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h | |||
@@ -1041,6 +1041,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *, | |||
1041 | struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t); | 1041 | struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t); |
1042 | struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *, | 1042 | struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *, |
1043 | struct rxrpc_peer *); | 1043 | struct rxrpc_peer *); |
1044 | void rxrpc_destroy_all_peers(struct rxrpc_net *); | ||
1044 | struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *); | 1045 | struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *); |
1045 | struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *); | 1046 | struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *); |
1046 | void rxrpc_put_peer(struct rxrpc_peer *); | 1047 | void rxrpc_put_peer(struct rxrpc_peer *); |
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index fa9ce60e7bfa..c7a023fb22d0 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c | |||
@@ -118,6 +118,7 @@ static __net_exit void rxrpc_exit_net(struct net *net) | |||
118 | cancel_work_sync(&rxnet->peer_keepalive_work); | 118 | cancel_work_sync(&rxnet->peer_keepalive_work); |
119 | rxrpc_destroy_all_calls(rxnet); | 119 | rxrpc_destroy_all_calls(rxnet); |
120 | rxrpc_destroy_all_connections(rxnet); | 120 | rxrpc_destroy_all_connections(rxnet); |
121 | rxrpc_destroy_all_peers(rxnet); | ||
121 | rxrpc_destroy_all_locals(rxnet); | 122 | rxrpc_destroy_all_locals(rxnet); |
122 | proc_remove(rxnet->proc_net); | 123 | proc_remove(rxnet->proc_net); |
123 | } | 124 | } |
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index a4a750aea1e5..1b7e8107b3ae 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c | |||
@@ -463,6 +463,27 @@ void rxrpc_put_peer(struct rxrpc_peer *peer) | |||
463 | } | 463 | } |
464 | } | 464 | } |
465 | 465 | ||
466 | /* | ||
467 | * Make sure all peer records have been discarded. | ||
468 | */ | ||
469 | void rxrpc_destroy_all_peers(struct rxrpc_net *rxnet) | ||
470 | { | ||
471 | struct rxrpc_peer *peer; | ||
472 | int i; | ||
473 | |||
474 | for (i = 0; i < HASH_SIZE(rxnet->peer_hash); i++) { | ||
475 | if (hlist_empty(&rxnet->peer_hash[i])) | ||
476 | continue; | ||
477 | |||
478 | hlist_for_each_entry(peer, &rxnet->peer_hash[i], hash_link) { | ||
479 | pr_err("Leaked peer %u {%u} %pISp\n", | ||
480 | peer->debug_id, | ||
481 | atomic_read(&peer->usage), | ||
482 | &peer->srx.transport); | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | |||
466 | /** | 487 | /** |
467 | * rxrpc_kernel_get_peer - Get the peer address of a call | 488 | * rxrpc_kernel_get_peer - Get the peer address of a call |
468 | * @sock: The socket on which the call is in progress. | 489 | * @sock: The socket on which the call is in progress. |
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index a62980a80151..206e802ccbdc 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c | |||
@@ -586,6 +586,7 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, | |||
586 | atomic_inc_return(&rxrpc_debug_id)); | 586 | atomic_inc_return(&rxrpc_debug_id)); |
587 | /* The socket is now unlocked */ | 587 | /* The socket is now unlocked */ |
588 | 588 | ||
589 | rxrpc_put_peer(cp.peer); | ||
589 | _leave(" = %p\n", call); | 590 | _leave(" = %p\n", call); |
590 | return call; | 591 | return call; |
591 | } | 592 | } |