aboutsummaryrefslogtreecommitdiffstats
path: root/net/tipc/node.c
diff options
context:
space:
mode:
authorJon Paul Maloy <jon.maloy@ericsson.com>2016-02-24 11:10:48 -0500
committerDavid S. Miller <davem@davemloft.net>2016-02-25 17:04:48 -0500
commitd25a01257e422a4bdeb426f69529d57c73b235fe (patch)
tree09941b9ca9b6136a6f3a3cb0458cbfaf1656bad3 /net/tipc/node.c
parentb170997acedc6c11ed2ec07b8d415601e65bb452 (diff)
tipc: fix crash during node removal
When the TIPC module is unloaded, we have identified a race condition that allows a node reference counter to go to zero and the node instance being freed before the node timer is finished with accessing it. This leads to occasional crashes, especially in multi-namespace environments. The scenario goes as follows: CPU0:(node_stop) CPU1:(node_timeout) // ref == 2 1: if(!mod_timer()) 2: if (del_timer()) 3: tipc_node_put() // ref -> 1 4: tipc_node_put() // ref -> 0 5: kfree_rcu(node); 6: tipc_node_get(node) 7: // BOOM! We now clean up this functionality as follows: 1) We remove the node pointer from the node lookup table before we attempt deactivating the timer. This way, we reduce the risk that tipc_node_find() may obtain a valid pointer to an instance marked for deletion; a harmless but undesirable situation. 2) We use del_timer_sync() instead of del_timer() to safely deactivate the node timer without any risk that it might be reactivated by the timeout handler. There is no risk of deadlock here, since the two functions never touch the same spinlocks. 3: We remove a pointless tipc_node_get() + tipc_node_put() from the timeout handler. Reported-by: Zhijiang Hu <huzhijiang@gmail.com> Acked-by: Ying Xue <ying.xue@windriver.com> Signed-off-by: Jon Maloy <jon.maloy@ericsson.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/tipc/node.c')
-rw-r--r--net/tipc/node.c24
1 files changed, 11 insertions, 13 deletions
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 792bbcbb3eed..cdb79503d890 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -225,9 +225,10 @@ static unsigned int tipc_hashfn(u32 addr)
225 225
226static void tipc_node_kref_release(struct kref *kref) 226static void tipc_node_kref_release(struct kref *kref)
227{ 227{
228 struct tipc_node *node = container_of(kref, struct tipc_node, kref); 228 struct tipc_node *n = container_of(kref, struct tipc_node, kref);
229 229
230 tipc_node_delete(node); 230 kfree(n->bc_entry.link);
231 kfree_rcu(n, rcu);
231} 232}
232 233
233static void tipc_node_put(struct tipc_node *node) 234static void tipc_node_put(struct tipc_node *node)
@@ -395,21 +396,20 @@ static void tipc_node_delete(struct tipc_node *node)
395{ 396{
396 list_del_rcu(&node->list); 397 list_del_rcu(&node->list);
397 hlist_del_rcu(&node->hash); 398 hlist_del_rcu(&node->hash);
398 kfree(node->bc_entry.link); 399 tipc_node_put(node);
399 kfree_rcu(node, rcu); 400
401 del_timer_sync(&node->timer);
402 tipc_node_put(node);
400} 403}
401 404
402void tipc_node_stop(struct net *net) 405void tipc_node_stop(struct net *net)
403{ 406{
404 struct tipc_net *tn = net_generic(net, tipc_net_id); 407 struct tipc_net *tn = tipc_net(net);
405 struct tipc_node *node, *t_node; 408 struct tipc_node *node, *t_node;
406 409
407 spin_lock_bh(&tn->node_list_lock); 410 spin_lock_bh(&tn->node_list_lock);
408 list_for_each_entry_safe(node, t_node, &tn->node_list, list) { 411 list_for_each_entry_safe(node, t_node, &tn->node_list, list)
409 if (del_timer(&node->timer)) 412 tipc_node_delete(node);
410 tipc_node_put(node);
411 tipc_node_put(node);
412 }
413 spin_unlock_bh(&tn->node_list_lock); 413 spin_unlock_bh(&tn->node_list_lock);
414} 414}
415 415
@@ -530,9 +530,7 @@ static void tipc_node_timeout(unsigned long data)
530 if (rc & TIPC_LINK_DOWN_EVT) 530 if (rc & TIPC_LINK_DOWN_EVT)
531 tipc_node_link_down(n, bearer_id, false); 531 tipc_node_link_down(n, bearer_id, false);
532 } 532 }
533 if (!mod_timer(&n->timer, jiffies + n->keepalive_intv)) 533 mod_timer(&n->timer, jiffies + n->keepalive_intv);
534 tipc_node_get(n);
535 tipc_node_put(n);
536} 534}
537 535
538/** 536/**