Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jon Paul Maloy and committed by
David S. Miller
b170997a 3da7611f

+9 -9
+9 -9
net/tipc/node.c
··· 245 245 */ 246 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 249 struct tipc_node *node; 250 + unsigned int thash = tipc_hashfn(addr); 250 251 251 252 if (unlikely(!in_own_cluster_exact(net, addr))) 252 253 return NULL; 253 254 254 255 rcu_read_lock(); 255 - hlist_for_each_entry_rcu(node, &tn->node_htable[tipc_hashfn(addr)], 256 - hash) { 257 - if (node->addr == addr) { 258 - tipc_node_get(node); 259 - rcu_read_unlock(); 260 - return node; 261 - } 256 + hlist_for_each_entry_rcu(node, &tn->node_htable[thash], hash) { 257 + if (node->addr != addr) 258 + continue; 259 + if (!kref_get_unless_zero(&node->kref)) 260 + node = NULL; 261 + break; 262 262 } 263 263 rcu_read_unlock(); 264 - return NULL; 264 + return node; 265 265 } 266 266 267 267 static void tipc_node_read_lock(struct tipc_node *n)