Commit b170997a authored by Jon Paul Maloy's avatar Jon Paul Maloy Committed by David S. Miller

tipc: eliminate risk of finding to-be-deleted node instance

Although we have never seen it happen, we have identified the
following problematic scenario when nodes are stopped and deleted:

CPU0:                            CPU1:

tipc_node_xxx()                                   //ref == 1
   tipc_node_put()                                //ref -> 0
                                 tipc_node_find() // node still in table
       tipc_node_delete()
         list_del_rcu(n. list)
                                 tipc_node_get()  //ref -> 1, bad
         kfree_rcu()

                                 tipc_node_put() //ref to 0 again.
                                 kfree_rcu()     // BOOM!

We fix this by introducing use of the conditional kref_get_if_not_zero()
instead of kref_get() in the function tipc_node_find(). This eliminates
any risk of post-mortem access.
Reported-by: default avatarZhijiang Hu <huzhijiang@gmail.com>
Acked-by: default avatarYing Xue <ying.xue@windriver.com>
Signed-off-by: default avatarJon Maloy <jon.maloy@ericsson.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3da7611f
...@@ -245,23 +245,23 @@ static void tipc_node_get(struct tipc_node *node) ...@@ -245,23 +245,23 @@ static void tipc_node_get(struct tipc_node *node)
*/ */
static struct tipc_node *tipc_node_find(struct net *net, u32 addr) static struct tipc_node *tipc_node_find(struct net *net, u32 addr)
{ {
struct tipc_net *tn = net_generic(net, tipc_net_id); struct tipc_net *tn = tipc_net(net);
struct tipc_node *node; struct tipc_node *node;
unsigned int thash = tipc_hashfn(addr);
if (unlikely(!in_own_cluster_exact(net, addr))) if (unlikely(!in_own_cluster_exact(net, addr)))
return NULL; return NULL;
rcu_read_lock(); rcu_read_lock();
hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], hlist_for_each_entry_rcu(node, &tn->node_htable[thash], hash) {
hash) { if (node->addr != addr)
if (node->addr == addr) { continue;
tipc_node_get(node); if (!kref_get_unless_zero(&node->kref))
rcu_read_unlock(); node = NULL;
return node; break;
}
} }
rcu_read_unlock(); rcu_read_unlock();
return NULL; return node;
} }
static void tipc_node_read_lock(struct tipc_node *n) static void tipc_node_read_lock(struct tipc_node *n)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment