aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2017-02-11 06:26:46 -0500
committerDavid S. Miller <davem@davemloft.net>2017-02-17 12:28:35 -0500
commit40f9f439706073b4b0a654b3b99e18296b7990b3 (patch)
treebaa437731befae16bef34595d72e2b64c2e4a97b /net/tipc
parent98687f426bb3e93b9dbbfb614ba331192beae482 (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.c4
-rw-r--r--net/tipc/socket.c30
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,
2234void tipc_sk_reinit(struct net *net) 2239void 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
2257static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid) 2265static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid)