diff options
author | David Howells <dhowells@redhat.com> | 2018-03-30 16:05:23 -0400 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2018-03-30 16:05:23 -0400 |
commit | d3be4d244330f7ef53242d8dc1b7f77d105e767f (patch) | |
tree | 794c9aeba90ac11e8d6a3109df89ad966adc73f6 | |
parent | 88f2a8257c9aa7df957b1a79a104f348d60d8027 (diff) |
rxrpc: Fix potential call vs socket/net destruction race
rxrpc_call structs don't pin sockets or network namespaces, but may attempt
to access both after their refcount reaches 0 so that they can detach
themselves from the network namespace. However, there's no guarantee that
the socket still exists at this point (so sock_net(&call->socket->sk) may
be invalid) and the namespace may have gone away if the call isn't pinning
a peer.
Fix this by (a) carrying a net pointer in the rxrpc_call struct and (b)
waiting for all calls to be destroyed when the network namespace goes away.
This was detected by checker:
net/rxrpc/call_object.c:634:57: warning: incorrect type in argument 1 (different address spaces)
net/rxrpc/call_object.c:634:57: expected struct sock const *sk
net/rxrpc/call_object.c:634:57: got struct sock [noderef] <asn:4>*<noident>
Fixes: 2baec2c3f854 ("rxrpc: Support network namespacing")
Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r-- | net/rxrpc/ar-internal.h | 2 | ||||
-rw-r--r-- | net/rxrpc/call_accept.c | 1 | ||||
-rw-r--r-- | net/rxrpc/call_object.c | 16 | ||||
-rw-r--r-- | net/rxrpc/net_ns.c | 1 |
4 files changed, 17 insertions, 3 deletions
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8a348e0a9d95..2a2b0fdfb157 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h | |||
@@ -75,6 +75,7 @@ struct rxrpc_net { | |||
75 | u32 epoch; /* Local epoch for detecting local-end reset */ | 75 | u32 epoch; /* Local epoch for detecting local-end reset */ |
76 | struct list_head calls; /* List of calls active in this namespace */ | 76 | struct list_head calls; /* List of calls active in this namespace */ |
77 | rwlock_t call_lock; /* Lock for ->calls */ | 77 | rwlock_t call_lock; /* Lock for ->calls */ |
78 | atomic_t nr_calls; /* Count of allocated calls */ | ||
78 | 79 | ||
79 | struct list_head conn_proc_list; /* List of conns in this namespace for proc */ | 80 | struct list_head conn_proc_list; /* List of conns in this namespace for proc */ |
80 | struct list_head service_conns; /* Service conns in this namespace */ | 81 | struct list_head service_conns; /* Service conns in this namespace */ |
@@ -528,6 +529,7 @@ struct rxrpc_call { | |||
528 | struct rxrpc_connection *conn; /* connection carrying call */ | 529 | struct rxrpc_connection *conn; /* connection carrying call */ |
529 | struct rxrpc_peer *peer; /* Peer record for remote address */ | 530 | struct rxrpc_peer *peer; /* Peer record for remote address */ |
530 | struct rxrpc_sock __rcu *socket; /* socket responsible */ | 531 | struct rxrpc_sock __rcu *socket; /* socket responsible */ |
532 | struct rxrpc_net *rxnet; /* Network namespace to which call belongs */ | ||
531 | struct mutex user_mutex; /* User access mutex */ | 533 | struct mutex user_mutex; /* User access mutex */ |
532 | unsigned long ack_at; /* When deferred ACK needs to happen */ | 534 | unsigned long ack_at; /* When deferred ACK needs to happen */ |
533 | unsigned long ack_lost_at; /* When ACK is figured as lost */ | 535 | unsigned long ack_lost_at; /* When ACK is figured as lost */ |
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 4ce24c000653..493545033e42 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c | |||
@@ -138,6 +138,7 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx, | |||
138 | 138 | ||
139 | write_unlock(&rx->call_lock); | 139 | write_unlock(&rx->call_lock); |
140 | 140 | ||
141 | rxnet = call->rxnet; | ||
141 | write_lock(&rxnet->call_lock); | 142 | write_lock(&rxnet->call_lock); |
142 | list_add_tail(&call->link, &rxnet->calls); | 143 | list_add_tail(&call->link, &rxnet->calls); |
143 | write_unlock(&rxnet->call_lock); | 144 | write_unlock(&rxnet->call_lock); |
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index 85b12c472522..f721c2b7e234 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c | |||
@@ -103,6 +103,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, | |||
103 | unsigned int debug_id) | 103 | unsigned int debug_id) |
104 | { | 104 | { |
105 | struct rxrpc_call *call; | 105 | struct rxrpc_call *call; |
106 | struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk)); | ||
106 | 107 | ||
107 | call = kmem_cache_zalloc(rxrpc_call_jar, gfp); | 108 | call = kmem_cache_zalloc(rxrpc_call_jar, gfp); |
108 | if (!call) | 109 | if (!call) |
@@ -153,6 +154,9 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp, | |||
153 | 154 | ||
154 | call->cong_cwnd = 2; | 155 | call->cong_cwnd = 2; |
155 | call->cong_ssthresh = RXRPC_RXTX_BUFF_SIZE - 1; | 156 | call->cong_ssthresh = RXRPC_RXTX_BUFF_SIZE - 1; |
157 | |||
158 | call->rxnet = rxnet; | ||
159 | atomic_inc(&rxnet->nr_calls); | ||
156 | return call; | 160 | return call; |
157 | 161 | ||
158 | nomem_2: | 162 | nomem_2: |
@@ -222,7 +226,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, | |||
222 | __acquires(&call->user_mutex) | 226 | __acquires(&call->user_mutex) |
223 | { | 227 | { |
224 | struct rxrpc_call *call, *xcall; | 228 | struct rxrpc_call *call, *xcall; |
225 | struct rxrpc_net *rxnet = rxrpc_net(sock_net(&rx->sk)); | 229 | struct rxrpc_net *rxnet; |
226 | struct rb_node *parent, **pp; | 230 | struct rb_node *parent, **pp; |
227 | const void *here = __builtin_return_address(0); | 231 | const void *here = __builtin_return_address(0); |
228 | int ret; | 232 | int ret; |
@@ -272,6 +276,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, | |||
272 | 276 | ||
273 | write_unlock(&rx->call_lock); | 277 | write_unlock(&rx->call_lock); |
274 | 278 | ||
279 | rxnet = call->rxnet; | ||
275 | write_lock(&rxnet->call_lock); | 280 | write_lock(&rxnet->call_lock); |
276 | list_add_tail(&call->link, &rxnet->calls); | 281 | list_add_tail(&call->link, &rxnet->calls); |
277 | write_unlock(&rxnet->call_lock); | 282 | write_unlock(&rxnet->call_lock); |
@@ -617,7 +622,7 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx) | |||
617 | */ | 622 | */ |
618 | void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op) | 623 | void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op) |
619 | { | 624 | { |
620 | struct rxrpc_net *rxnet; | 625 | struct rxrpc_net *rxnet = call->rxnet; |
621 | const void *here = __builtin_return_address(0); | 626 | const void *here = __builtin_return_address(0); |
622 | int n; | 627 | int n; |
623 | 628 | ||
@@ -631,7 +636,6 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op) | |||
631 | ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); | 636 | ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); |
632 | 637 | ||
633 | if (!list_empty(&call->link)) { | 638 | if (!list_empty(&call->link)) { |
634 | rxnet = rxrpc_net(sock_net(&call->socket->sk)); | ||
635 | write_lock(&rxnet->call_lock); | 639 | write_lock(&rxnet->call_lock); |
636 | list_del_init(&call->link); | 640 | list_del_init(&call->link); |
637 | write_unlock(&rxnet->call_lock); | 641 | write_unlock(&rxnet->call_lock); |
@@ -647,11 +651,14 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace op) | |||
647 | static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) | 651 | static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) |
648 | { | 652 | { |
649 | struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); | 653 | struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); |
654 | struct rxrpc_net *rxnet = call->rxnet; | ||
650 | 655 | ||
651 | rxrpc_put_peer(call->peer); | 656 | rxrpc_put_peer(call->peer); |
652 | kfree(call->rxtx_buffer); | 657 | kfree(call->rxtx_buffer); |
653 | kfree(call->rxtx_annotations); | 658 | kfree(call->rxtx_annotations); |
654 | kmem_cache_free(rxrpc_call_jar, call); | 659 | kmem_cache_free(rxrpc_call_jar, call); |
660 | if (atomic_dec_and_test(&rxnet->nr_calls)) | ||
661 | wake_up_atomic_t(&rxnet->nr_calls); | ||
655 | } | 662 | } |
656 | 663 | ||
657 | /* | 664 | /* |
@@ -716,4 +723,7 @@ void rxrpc_destroy_all_calls(struct rxrpc_net *rxnet) | |||
716 | } | 723 | } |
717 | 724 | ||
718 | write_unlock(&rxnet->call_lock); | 725 | write_unlock(&rxnet->call_lock); |
726 | |||
727 | atomic_dec(&rxnet->nr_calls); | ||
728 | wait_on_atomic_t(&rxnet->nr_calls, atomic_t_wait, TASK_UNINTERRUPTIBLE); | ||
719 | } | 729 | } |
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c index 66baf2b80b6c..101019b0be34 100644 --- a/net/rxrpc/net_ns.c +++ b/net/rxrpc/net_ns.c | |||
@@ -55,6 +55,7 @@ static __net_init int rxrpc_init_net(struct net *net) | |||
55 | 55 | ||
56 | INIT_LIST_HEAD(&rxnet->calls); | 56 | INIT_LIST_HEAD(&rxnet->calls); |
57 | rwlock_init(&rxnet->call_lock); | 57 | rwlock_init(&rxnet->call_lock); |
58 | atomic_set(&rxnet->nr_calls, 1); | ||
58 | 59 | ||
59 | INIT_LIST_HEAD(&rxnet->conn_proc_list); | 60 | INIT_LIST_HEAD(&rxnet->conn_proc_list); |
60 | INIT_LIST_HEAD(&rxnet->service_conns); | 61 | INIT_LIST_HEAD(&rxnet->service_conns); |