aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv
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
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')
-rw-r--r--net/batman-adv/bat_sysfs.c20
-rw-r--r--net/batman-adv/hard-interface.c40
-rw-r--r--net/batman-adv/hard-interface.h9
-rw-r--r--net/batman-adv/types.h2
4 files changed, 33 insertions, 38 deletions
diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c
index f7b93a0805fe..93ae20aaad0a 100644
--- a/net/batman-adv/bat_sysfs.c
+++ b/net/batman-adv/bat_sysfs.c
@@ -450,7 +450,7 @@ static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr,
450 length = sprintf(buff, "%s\n", batman_if->if_status == IF_NOT_IN_USE ? 450 length = sprintf(buff, "%s\n", batman_if->if_status == IF_NOT_IN_USE ?
451 "none" : batman_if->soft_iface->name); 451 "none" : batman_if->soft_iface->name);
452 452
453 kref_put(&batman_if->refcount, hardif_free_ref); 453 hardif_free_ref(batman_if);
454 454
455 return length; 455 return length;
456} 456}
@@ -461,7 +461,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
461 struct net_device *net_dev = kobj_to_netdev(kobj); 461 struct net_device *net_dev = kobj_to_netdev(kobj);
462 struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); 462 struct batman_if *batman_if = get_batman_if_by_netdev(net_dev);
463 int status_tmp = -1; 463 int status_tmp = -1;
464 int ret; 464 int ret = count;
465 465
466 if (!batman_if) 466 if (!batman_if)
467 return count; 467 return count;
@@ -472,7 +472,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
472 if (strlen(buff) >= IFNAMSIZ) { 472 if (strlen(buff) >= IFNAMSIZ) {
473 pr_err("Invalid parameter for 'mesh_iface' setting received: " 473 pr_err("Invalid parameter for 'mesh_iface' setting received: "
474 "interface name too long '%s'\n", buff); 474 "interface name too long '%s'\n", buff);
475 kref_put(&batman_if->refcount, hardif_free_ref); 475 hardif_free_ref(batman_if);
476 return -EINVAL; 476 return -EINVAL;
477 } 477 }
478 478
@@ -482,17 +482,14 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
482 status_tmp = IF_I_WANT_YOU; 482 status_tmp = IF_I_WANT_YOU;
483 483
484 if ((batman_if->if_status == status_tmp) || ((batman_if->soft_iface) && 484 if ((batman_if->if_status == status_tmp) || ((batman_if->soft_iface) &&
485 (strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0))) { 485 (strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0)))
486 kref_put(&batman_if->refcount, hardif_free_ref); 486 goto out;
487 return count;
488 }
489 487
490 if (status_tmp == IF_NOT_IN_USE) { 488 if (status_tmp == IF_NOT_IN_USE) {
491 rtnl_lock(); 489 rtnl_lock();
492 hardif_disable_interface(batman_if); 490 hardif_disable_interface(batman_if);
493 rtnl_unlock(); 491 rtnl_unlock();
494 kref_put(&batman_if->refcount, hardif_free_ref); 492 goto out;
495 return count;
496 } 493 }
497 494
498 /* if the interface already is in use */ 495 /* if the interface already is in use */
@@ -503,8 +500,9 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr,
503 } 500 }
504 501
505 ret = hardif_enable_interface(batman_if, buff); 502 ret = hardif_enable_interface(batman_if, buff);
506 kref_put(&batman_if->refcount, hardif_free_ref);
507 503
504out:
505 hardif_free_ref(batman_if);
508 return ret; 506 return ret;
509} 507}
510 508
@@ -537,7 +535,7 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr,
537 break; 535 break;
538 } 536 }
539 537
540 kref_put(&batman_if->refcount, hardif_free_ref); 538 hardif_free_ref(batman_if);
541 539
542 return length; 540 return length;
543} 541}
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}
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index ad195438428a..e488b90b8fea 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -37,13 +37,12 @@ void hardif_disable_interface(struct batman_if *batman_if);
37void hardif_remove_interfaces(void); 37void hardif_remove_interfaces(void);
38int hardif_min_mtu(struct net_device *soft_iface); 38int hardif_min_mtu(struct net_device *soft_iface);
39void update_min_mtu(struct net_device *soft_iface); 39void update_min_mtu(struct net_device *soft_iface);
40void hardif_free_rcu(struct rcu_head *rcu);
40 41
41static inline void hardif_free_ref(struct kref *refcount) 42static inline void hardif_free_ref(struct batman_if *batman_if)
42{ 43{
43 struct batman_if *batman_if; 44 if (atomic_dec_and_test(&batman_if->refcount))
44 45 call_rcu(&batman_if->rcu, hardif_free_rcu);
45 batman_if = container_of(refcount, struct batman_if, refcount);
46 kfree(batman_if);
47} 46}
48 47
49#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ 48#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 96f7c224975b..e0140c65d8f3 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -43,7 +43,7 @@ struct batman_if {
43 unsigned char *packet_buff; 43 unsigned char *packet_buff;
44 int packet_len; 44 int packet_len;
45 struct kobject *hardif_obj; 45 struct kobject *hardif_obj;
46 struct kref refcount; 46 atomic_t refcount;
47 struct packet_type batman_adv_ptype; 47 struct packet_type batman_adv_ptype;
48 struct net_device *soft_iface; 48 struct net_device *soft_iface;
49 struct rcu_head rcu; 49 struct rcu_head rcu;