diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2017-02-11 06:26:46 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-02-17 12:28:35 -0500 |
commit | 40f9f439706073b4b0a654b3b99e18296b7990b3 (patch) | |
tree | baa437731befae16bef34595d72e2b64c2e4a97b /net/tipc | |
parent | 98687f426bb3e93b9dbbfb614ba331192beae482 (diff) |
tipc: Fix tipc_sk_reinit race conditions
There are two problems with the function tipc_sk_reinit. Firstly
it's doing a manual walk over an rhashtable. This is broken as
an rhashtable can be resized and if you manually walk over it
during a resize then you may miss entries.
Secondly it's missing memory barriers as previously the code used
spinlocks which provide the barriers implicitly.
This patch fixes both problems.
Fixes: 07f6c4bc048a ("tipc: convert tipc reference table to...")
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Acked-by: Ying Xue <ying.xue@windriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc')
-rw-r--r-- | net/tipc/net.c | 4 | ||||
-rw-r--r-- | net/tipc/socket.c | 30 |
2 files changed, 23 insertions, 11 deletions
diff --git a/net/tipc/net.c b/net/tipc/net.c index 28bf4feeb81c..ab8a2d5d1e32 100644 --- a/net/tipc/net.c +++ b/net/tipc/net.c | |||
@@ -110,6 +110,10 @@ int tipc_net_start(struct net *net, u32 addr) | |||
110 | char addr_string[16]; | 110 | char addr_string[16]; |
111 | 111 | ||
112 | tn->own_addr = addr; | 112 | tn->own_addr = addr; |
113 | |||
114 | /* Ensure that the new address is visible before we reinit. */ | ||
115 | smp_mb(); | ||
116 | |||
113 | tipc_named_reinit(net); | 117 | tipc_named_reinit(net); |
114 | tipc_sk_reinit(net); | 118 | tipc_sk_reinit(net); |
115 | 119 | ||
diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 103d1fd058c0..6b09a778cc71 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c | |||
@@ -430,8 +430,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock, | |||
430 | INIT_LIST_HEAD(&tsk->cong_links); | 430 | INIT_LIST_HEAD(&tsk->cong_links); |
431 | msg = &tsk->phdr; | 431 | msg = &tsk->phdr; |
432 | tn = net_generic(sock_net(sk), tipc_net_id); | 432 | tn = net_generic(sock_net(sk), tipc_net_id); |
433 | tipc_msg_init(tn->own_addr, msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, | ||
434 | NAMED_H_SIZE, 0); | ||
435 | 433 | ||
436 | /* Finish initializing socket data structures */ | 434 | /* Finish initializing socket data structures */ |
437 | sock->ops = ops; | 435 | sock->ops = ops; |
@@ -441,6 +439,13 @@ static int tipc_sk_create(struct net *net, struct socket *sock, | |||
441 | pr_warn("Socket create failed; port number exhausted\n"); | 439 | pr_warn("Socket create failed; port number exhausted\n"); |
442 | return -EINVAL; | 440 | return -EINVAL; |
443 | } | 441 | } |
442 | |||
443 | /* Ensure tsk is visible before we read own_addr. */ | ||
444 | smp_mb(); | ||
445 | |||
446 | tipc_msg_init(tn->own_addr, msg, TIPC_LOW_IMPORTANCE, TIPC_NAMED_MSG, | ||
447 | NAMED_H_SIZE, 0); | ||
448 | |||
444 | msg_set_origport(msg, tsk->portid); | 449 | msg_set_origport(msg, tsk->portid); |
445 | setup_timer(&sk->sk_timer, tipc_sk_timeout, (unsigned long)tsk); | 450 | setup_timer(&sk->sk_timer, tipc_sk_timeout, (unsigned long)tsk); |
446 | sk->sk_shutdown = 0; | 451 | sk->sk_shutdown = 0; |
@@ -2234,24 +2239,27 @@ static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, | |||
2234 | void tipc_sk_reinit(struct net *net) | 2239 | void tipc_sk_reinit(struct net *net) |
2235 | { | 2240 | { |
2236 | struct tipc_net *tn = net_generic(net, tipc_net_id); | 2241 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
2237 | const struct bucket_table *tbl; | 2242 | struct rhashtable_iter iter; |
2238 | struct rhash_head *pos; | ||
2239 | struct tipc_sock *tsk; | 2243 | struct tipc_sock *tsk; |
2240 | struct tipc_msg *msg; | 2244 | struct tipc_msg *msg; |
2241 | int i; | ||
2242 | 2245 | ||
2243 | rcu_read_lock(); | 2246 | rhashtable_walk_enter(&tn->sk_rht, &iter); |
2244 | tbl = rht_dereference_rcu((&tn->sk_rht)->tbl, &tn->sk_rht); | 2247 | |
2245 | for (i = 0; i < tbl->size; i++) { | 2248 | do { |
2246 | rht_for_each_entry_rcu(tsk, pos, tbl, i, node) { | 2249 | tsk = ERR_PTR(rhashtable_walk_start(&iter)); |
2250 | if (tsk) | ||
2251 | continue; | ||
2252 | |||
2253 | while ((tsk = rhashtable_walk_next(&iter)) && !IS_ERR(tsk)) { | ||
2247 | spin_lock_bh(&tsk->sk.sk_lock.slock); | 2254 | spin_lock_bh(&tsk->sk.sk_lock.slock); |
2248 | msg = &tsk->phdr; | 2255 | msg = &tsk->phdr; |
2249 | msg_set_prevnode(msg, tn->own_addr); | 2256 | msg_set_prevnode(msg, tn->own_addr); |
2250 | msg_set_orignode(msg, tn->own_addr); | 2257 | msg_set_orignode(msg, tn->own_addr); |
2251 | spin_unlock_bh(&tsk->sk.sk_lock.slock); | 2258 | spin_unlock_bh(&tsk->sk.sk_lock.slock); |
2252 | } | 2259 | } |
2253 | } | 2260 | |
2254 | rcu_read_unlock(); | 2261 | rhashtable_walk_stop(&iter); |
2262 | } while (tsk == ERR_PTR(-EAGAIN)); | ||
2255 | } | 2263 | } |
2256 | 2264 | ||
2257 | static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid) | 2265 | static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid) |