aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv/hard-interface.c
diff options
context:
space:
mode:
authorMarek Lindner <lindner_marek@yahoo.de>2011-02-10 09:33:51 -0500
committerMarek Lindner <lindner_marek@yahoo.de>2011-03-05 06:50:07 -0500
commited75ccbe26f4a672a41556120390e67c80a2c441 (patch)
tree3f4f844d2311e662b4c42e7a275188d1346ac77d /net/batman-adv/hard-interface.c
parent7d2b554826195372764910da2f0dcb0d9b869108 (diff)
batman-adv: Correct rcu refcounting for batman_if
It might be possible that 2 threads access the same data in the same rcu grace period. The first thread calls call_rcu() to decrement the refcount and free the data while the second thread increases the refcount to use the data. To avoid this race condition all refcount operations have to be atomic. Reported-by: Sven Eckelmann <sven@narfation.org> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Diffstat (limited to 'net/batman-adv/hard-interface.c')
-rw-r--r--net/batman-adv/hard-interface.c40
1 files changed, 19 insertions, 21 deletions
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index e2b001ad45c4..89824853d6e3 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -40,13 +40,13 @@ static int batman_skb_recv(struct sk_buff *skb,
40 struct packet_type *ptype, 40 struct packet_type *ptype,
41 struct net_device *orig_dev); 41 struct net_device *orig_dev);
42 42
43static void hardif_free_rcu(struct rcu_head *rcu) 43void hardif_free_rcu(struct rcu_head *rcu)
44{ 44{
45 struct batman_if *batman_if; 45 struct batman_if *batman_if;
46 46
47 batman_if = container_of(rcu, struct batman_if, rcu); 47 batman_if = container_of(rcu, struct batman_if, rcu);
48 dev_put(batman_if->net_dev); 48 dev_put(batman_if->net_dev);
49 kref_put(&batman_if->refcount, hardif_free_ref); 49 kfree(batman_if);
50} 50}
51 51
52struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) 52struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev)
@@ -55,16 +55,14 @@ struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev)
55 55
56 rcu_read_lock(); 56 rcu_read_lock();
57 list_for_each_entry_rcu(batman_if, &if_list, list) { 57 list_for_each_entry_rcu(batman_if, &if_list, list) {
58 if (batman_if->net_dev == net_dev) 58 if (batman_if->net_dev == net_dev &&
59 atomic_inc_not_zero(&batman_if->refcount))
59 goto out; 60 goto out;
60 } 61 }
61 62
62 batman_if = NULL; 63 batman_if = NULL;
63 64
64out: 65out:
65 if (batman_if)
66 kref_get(&batman_if->refcount);
67
68 rcu_read_unlock(); 66 rcu_read_unlock();
69 return batman_if; 67 return batman_if;
70} 68}
@@ -105,16 +103,14 @@ static struct batman_if *get_active_batman_if(struct net_device *soft_iface)
105 if (batman_if->soft_iface != soft_iface) 103 if (batman_if->soft_iface != soft_iface)
106 continue; 104 continue;
107 105
108 if (batman_if->if_status == IF_ACTIVE) 106 if (batman_if->if_status == IF_ACTIVE &&
107 atomic_inc_not_zero(&batman_if->refcount))
109 goto out; 108 goto out;
110 } 109 }
111 110
112 batman_if = NULL; 111 batman_if = NULL;
113 112
114out: 113out:
115 if (batman_if)
116 kref_get(&batman_if->refcount);
117
118 rcu_read_unlock(); 114 rcu_read_unlock();
119 return batman_if; 115 return batman_if;
120} 116}
@@ -137,14 +133,14 @@ static void set_primary_if(struct bat_priv *bat_priv,
137 struct batman_packet *batman_packet; 133 struct batman_packet *batman_packet;
138 struct batman_if *old_if; 134 struct batman_if *old_if;
139 135
140 if (batman_if) 136 if (batman_if && !atomic_inc_not_zero(&batman_if->refcount))
141 kref_get(&batman_if->refcount); 137 batman_if = NULL;
142 138
143 old_if = bat_priv->primary_if; 139 old_if = bat_priv->primary_if;
144 bat_priv->primary_if = batman_if; 140 bat_priv->primary_if = batman_if;
145 141
146 if (old_if) 142 if (old_if)
147 kref_put(&old_if->refcount, hardif_free_ref); 143 hardif_free_ref(old_if);
148 144
149 if (!bat_priv->primary_if) 145 if (!bat_priv->primary_if)
150 return; 146 return;
@@ -290,6 +286,9 @@ int hardif_enable_interface(struct batman_if *batman_if, char *iface_name)
290 if (batman_if->if_status != IF_NOT_IN_USE) 286 if (batman_if->if_status != IF_NOT_IN_USE)
291 goto out; 287 goto out;
292 288
289 if (!atomic_inc_not_zero(&batman_if->refcount))
290 goto out;
291
293 batman_if->soft_iface = dev_get_by_name(&init_net, iface_name); 292 batman_if->soft_iface = dev_get_by_name(&init_net, iface_name);
294 293
295 if (!batman_if->soft_iface) { 294 if (!batman_if->soft_iface) {
@@ -328,7 +327,6 @@ int hardif_enable_interface(struct batman_if *batman_if, char *iface_name)
328 batman_if->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN); 327 batman_if->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN);
329 batman_if->batman_adv_ptype.func = batman_skb_recv; 328 batman_if->batman_adv_ptype.func = batman_skb_recv;
330 batman_if->batman_adv_ptype.dev = batman_if->net_dev; 329 batman_if->batman_adv_ptype.dev = batman_if->net_dev;
331 kref_get(&batman_if->refcount);
332 dev_add_pack(&batman_if->batman_adv_ptype); 330 dev_add_pack(&batman_if->batman_adv_ptype);
333 331
334 atomic_set(&batman_if->seqno, 1); 332 atomic_set(&batman_if->seqno, 1);
@@ -371,6 +369,7 @@ out:
371 return 0; 369 return 0;
372 370
373err: 371err:
372 hardif_free_ref(batman_if);
374 return -ENOMEM; 373 return -ENOMEM;
375} 374}
376 375
@@ -387,7 +386,6 @@ void hardif_disable_interface(struct batman_if *batman_if)
387 bat_info(batman_if->soft_iface, "Removing interface: %s\n", 386 bat_info(batman_if->soft_iface, "Removing interface: %s\n",
388 batman_if->net_dev->name); 387 batman_if->net_dev->name);
389 dev_remove_pack(&batman_if->batman_adv_ptype); 388 dev_remove_pack(&batman_if->batman_adv_ptype);
390 kref_put(&batman_if->refcount, hardif_free_ref);
391 389
392 bat_priv->num_ifaces--; 390 bat_priv->num_ifaces--;
393 orig_hash_del_if(batman_if, bat_priv->num_ifaces); 391 orig_hash_del_if(batman_if, bat_priv->num_ifaces);
@@ -399,7 +397,7 @@ void hardif_disable_interface(struct batman_if *batman_if)
399 set_primary_if(bat_priv, new_if); 397 set_primary_if(bat_priv, new_if);
400 398
401 if (new_if) 399 if (new_if)
402 kref_put(&new_if->refcount, hardif_free_ref); 400 hardif_free_ref(new_if);
403 } 401 }
404 402
405 kfree(batman_if->packet_buff); 403 kfree(batman_if->packet_buff);
@@ -416,6 +414,7 @@ void hardif_disable_interface(struct batman_if *batman_if)
416 softif_destroy(batman_if->soft_iface); 414 softif_destroy(batman_if->soft_iface);
417 415
418 batman_if->soft_iface = NULL; 416 batman_if->soft_iface = NULL;
417 hardif_free_ref(batman_if);
419} 418}
420 419
421static struct batman_if *hardif_add_interface(struct net_device *net_dev) 420static struct batman_if *hardif_add_interface(struct net_device *net_dev)
@@ -445,7 +444,8 @@ static struct batman_if *hardif_add_interface(struct net_device *net_dev)
445 batman_if->soft_iface = NULL; 444 batman_if->soft_iface = NULL;
446 batman_if->if_status = IF_NOT_IN_USE; 445 batman_if->if_status = IF_NOT_IN_USE;
447 INIT_LIST_HEAD(&batman_if->list); 446 INIT_LIST_HEAD(&batman_if->list);
448 kref_init(&batman_if->refcount); 447 /* extra reference for return */
448 atomic_set(&batman_if->refcount, 2);
449 449
450 check_known_mac_addr(batman_if->net_dev); 450 check_known_mac_addr(batman_if->net_dev);
451 451
@@ -453,8 +453,6 @@ static struct batman_if *hardif_add_interface(struct net_device *net_dev)
453 list_add_tail_rcu(&batman_if->list, &if_list); 453 list_add_tail_rcu(&batman_if->list, &if_list);
454 spin_unlock(&if_list_lock); 454 spin_unlock(&if_list_lock);
455 455
456 /* extra reference for return */
457 kref_get(&batman_if->refcount);
458 return batman_if; 456 return batman_if;
459 457
460free_if: 458free_if:
@@ -476,7 +474,7 @@ static void hardif_remove_interface(struct batman_if *batman_if)
476 474
477 batman_if->if_status = IF_TO_BE_REMOVED; 475 batman_if->if_status = IF_TO_BE_REMOVED;
478 sysfs_del_hardif(&batman_if->hardif_obj); 476 sysfs_del_hardif(&batman_if->hardif_obj);
479 call_rcu(&batman_if->rcu, hardif_free_rcu); 477 hardif_free_ref(batman_if);
480} 478}
481 479
482void hardif_remove_interfaces(void) 480void hardif_remove_interfaces(void)
@@ -548,7 +546,7 @@ static int hard_if_event(struct notifier_block *this,
548 }; 546 };
549 547
550hardif_put: 548hardif_put:
551 kref_put(&batman_if->refcount, hardif_free_ref); 549 hardif_free_ref(batman_if);
552out: 550out:
553 return NOTIFY_DONE; 551 return NOTIFY_DONE;
554} 552}