diff options
author | Jon Paul Maloy <jon.maloy@ericsson.com> | 2016-02-24 11:00:19 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-02-25 17:04:48 -0500 |
commit | b170997acedc6c11ed2ec07b8d415601e65bb452 (patch) | |
tree | b0759cbe4ceec6d799d8830fd03f3fd5e357a271 /net/tipc/node.c | |
parent | 3da7611f4285e0434dff62bcabb8b5bfdae5915f (diff) |
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: 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.c | 18 |
1 files changed, 9 insertions, 9 deletions
diff --git a/net/tipc/node.c b/net/tipc/node.c index 9fcc2fb0ee00..792bbcbb3eed 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c | |||
@@ -245,23 +245,23 @@ static void tipc_node_get(struct tipc_node *node) | |||
245 | */ | 245 | */ |
246 | static struct tipc_node *tipc_node_find(struct net *net, u32 addr) | 246 | static struct tipc_node *tipc_node_find(struct net *net, u32 addr) |
247 | { | 247 | { |
248 | struct tipc_net *tn = net_generic(net, tipc_net_id); | 248 | struct tipc_net *tn = tipc_net(net); |
249 | struct tipc_node *node; | 249 | struct tipc_node *node; |
250 | unsigned int thash = tipc_hashfn(addr); | ||
250 | 251 | ||
251 | if (unlikely(!in_own_cluster_exact(net, addr))) | 252 | if (unlikely(!in_own_cluster_exact(net, addr))) |
252 | return NULL; | 253 | return NULL; |
253 | 254 | ||
254 | rcu_read_lock(); | 255 | rcu_read_lock(); |
255 | hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], | 256 | hlist_for_each_entry_rcu(node, &tn->node_htable[thash], hash) { |
256 | hash) { | 257 | if (node->addr != addr) |
257 | if (node->addr == addr) { | 258 | continue; |
258 | tipc_node_get(node); | 259 | if (!kref_get_unless_zero(&node->kref)) |
259 | rcu_read_unlock(); | 260 | node = NULL; |
260 | return node; | 261 | break; |
261 | } | ||
262 | } | 262 | } |
263 | rcu_read_unlock(); | 263 | rcu_read_unlock(); |
264 | return NULL; | 264 | return node; |
265 | } | 265 | } |
266 | 266 | ||
267 | static void tipc_node_read_lock(struct tipc_node *n) | 267 | static void tipc_node_read_lock(struct tipc_node *n) |