aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv
diff options
context:
space:
mode:
authorMarek Lindner <lindner_marek@yahoo.de>2011-02-18 07:28:10 -0500
committerMarek Lindner <lindner_marek@yahoo.de>2011-03-05 06:52:01 -0500
commit7b36e8eef989fc59535b4f1d3fc0f83afaf419d4 (patch)
treef0900101542966e0655ca5f115b5b0bc409b1e74 /net/batman-adv
parent7aadf889e897155c45cda230d2a6701ad1fbff61 (diff)
batman-adv: Correct rcu refcounting for orig_node
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/gateway_client.c6
-rw-r--r--net/batman-adv/icmp_socket.c2
-rw-r--r--net/batman-adv/originator.c21
-rw-r--r--net/batman-adv/originator.h6
-rw-r--r--net/batman-adv/routing.c24
-rw-r--r--net/batman-adv/translation-table.c15
-rw-r--r--net/batman-adv/types.h3
-rw-r--r--net/batman-adv/unicast.c6
-rw-r--r--net/batman-adv/vis.c2
9 files changed, 49 insertions, 36 deletions
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 41eba8a660f5..3cc43558cf9c 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -53,9 +53,11 @@ void *gw_get_selected(struct bat_priv *bat_priv)
53 goto out; 53 goto out;
54 54
55 orig_node = curr_gateway_tmp->orig_node; 55 orig_node = curr_gateway_tmp->orig_node;
56 if (!orig_node)
57 goto out;
56 58
57 if (orig_node) 59 if (!atomic_inc_not_zero(&orig_node->refcount))
58 kref_get(&orig_node->refcount); 60 orig_node = NULL;
59 61
60out: 62out:
61 rcu_read_unlock(); 63 rcu_read_unlock();
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index 139b7336adf9..a0a35b1af167 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -271,7 +271,7 @@ out:
271 if (neigh_node) 271 if (neigh_node)
272 neigh_node_free_ref(neigh_node); 272 neigh_node_free_ref(neigh_node);
273 if (orig_node) 273 if (orig_node)
274 kref_put(&orig_node->refcount, orig_node_free_ref); 274 orig_node_free_ref(orig_node);
275 return len; 275 return len;
276} 276}
277 277
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index bdcb399329dd..a70debebfc5b 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -102,13 +102,13 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node,
102 return neigh_node; 102 return neigh_node;
103} 103}
104 104
105void orig_node_free_ref(struct kref *refcount) 105static void orig_node_free_rcu(struct rcu_head *rcu)
106{ 106{
107 struct hlist_node *node, *node_tmp; 107 struct hlist_node *node, *node_tmp;
108 struct neigh_node *neigh_node, *tmp_neigh_node; 108 struct neigh_node *neigh_node, *tmp_neigh_node;
109 struct orig_node *orig_node; 109 struct orig_node *orig_node;
110 110
111 orig_node = container_of(refcount, struct orig_node, refcount); 111 orig_node = container_of(rcu, struct orig_node, rcu);
112 112
113 spin_lock_bh(&orig_node->neigh_list_lock); 113 spin_lock_bh(&orig_node->neigh_list_lock);
114 114
@@ -137,6 +137,12 @@ void orig_node_free_ref(struct kref *refcount)
137 kfree(orig_node); 137 kfree(orig_node);
138} 138}
139 139
140void orig_node_free_ref(struct orig_node *orig_node)
141{
142 if (atomic_dec_and_test(&orig_node->refcount))
143 call_rcu(&orig_node->rcu, orig_node_free_rcu);
144}
145
140void originator_free(struct bat_priv *bat_priv) 146void originator_free(struct bat_priv *bat_priv)
141{ 147{
142 struct hashtable_t *hash = bat_priv->orig_hash; 148 struct hashtable_t *hash = bat_priv->orig_hash;
@@ -163,7 +169,7 @@ void originator_free(struct bat_priv *bat_priv)
163 head, hash_entry) { 169 head, hash_entry) {
164 170
165 hlist_del_rcu(node); 171 hlist_del_rcu(node);
166 kref_put(&orig_node->refcount, orig_node_free_ref); 172 orig_node_free_ref(orig_node);
167 } 173 }
168 spin_unlock_bh(list_lock); 174 spin_unlock_bh(list_lock);
169 } 175 }
@@ -196,7 +202,9 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
196 spin_lock_init(&orig_node->ogm_cnt_lock); 202 spin_lock_init(&orig_node->ogm_cnt_lock);
197 spin_lock_init(&orig_node->bcast_seqno_lock); 203 spin_lock_init(&orig_node->bcast_seqno_lock);
198 spin_lock_init(&orig_node->neigh_list_lock); 204 spin_lock_init(&orig_node->neigh_list_lock);
199 kref_init(&orig_node->refcount); 205
206 /* extra reference for return */
207 atomic_set(&orig_node->refcount, 2);
200 208
201 orig_node->bat_priv = bat_priv; 209 orig_node->bat_priv = bat_priv;
202 memcpy(orig_node->orig, addr, ETH_ALEN); 210 memcpy(orig_node->orig, addr, ETH_ALEN);
@@ -229,8 +237,6 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
229 if (hash_added < 0) 237 if (hash_added < 0)
230 goto free_bcast_own_sum; 238 goto free_bcast_own_sum;
231 239
232 /* extra reference for return */
233 kref_get(&orig_node->refcount);
234 return orig_node; 240 return orig_node;
235free_bcast_own_sum: 241free_bcast_own_sum:
236 kfree(orig_node->bcast_own_sum); 242 kfree(orig_node->bcast_own_sum);
@@ -348,8 +354,7 @@ static void _purge_orig(struct bat_priv *bat_priv)
348 if (orig_node->gw_flags) 354 if (orig_node->gw_flags)
349 gw_node_delete(bat_priv, orig_node); 355 gw_node_delete(bat_priv, orig_node);
350 hlist_del_rcu(node); 356 hlist_del_rcu(node);
351 kref_put(&orig_node->refcount, 357 orig_node_free_ref(orig_node);
352 orig_node_free_ref);
353 continue; 358 continue;
354 } 359 }
355 360
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h
index b4b9a09259fd..3d7a39d4df0f 100644
--- a/net/batman-adv/originator.h
+++ b/net/batman-adv/originator.h
@@ -27,7 +27,7 @@
27int originator_init(struct bat_priv *bat_priv); 27int originator_init(struct bat_priv *bat_priv);
28void originator_free(struct bat_priv *bat_priv); 28void originator_free(struct bat_priv *bat_priv);
29void purge_orig_ref(struct bat_priv *bat_priv); 29void purge_orig_ref(struct bat_priv *bat_priv);
30void orig_node_free_ref(struct kref *refcount); 30void orig_node_free_ref(struct orig_node *orig_node);
31struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); 31struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr);
32struct neigh_node *create_neighbor(struct orig_node *orig_node, 32struct neigh_node *create_neighbor(struct orig_node *orig_node,
33 struct orig_node *orig_neigh_node, 33 struct orig_node *orig_neigh_node,
@@ -88,8 +88,10 @@ static inline struct orig_node *orig_hash_find(struct bat_priv *bat_priv,
88 if (!compare_eth(orig_node, data)) 88 if (!compare_eth(orig_node, data))
89 continue; 89 continue;
90 90
91 if (!atomic_inc_not_zero(&orig_node->refcount))
92 continue;
93
91 orig_node_tmp = orig_node; 94 orig_node_tmp = orig_node;
92 kref_get(&orig_node_tmp->refcount);
93 break; 95 break;
94 } 96 }
95 rcu_read_unlock(); 97 rcu_read_unlock();
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index fc4c12a049da..9863c03a2137 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -420,7 +420,7 @@ static void update_orig(struct bat_priv *bat_priv,
420 neigh_node = create_neighbor(orig_node, orig_tmp, 420 neigh_node = create_neighbor(orig_node, orig_tmp,
421 ethhdr->h_source, if_incoming); 421 ethhdr->h_source, if_incoming);
422 422
423 kref_put(&orig_tmp->refcount, orig_node_free_ref); 423 orig_node_free_ref(orig_tmp);
424 if (!neigh_node) 424 if (!neigh_node)
425 goto unlock; 425 goto unlock;
426 426
@@ -604,7 +604,7 @@ static char count_real_packets(struct ethhdr *ethhdr,
604 604
605out: 605out:
606 spin_unlock_bh(&orig_node->ogm_cnt_lock); 606 spin_unlock_bh(&orig_node->ogm_cnt_lock);
607 kref_put(&orig_node->refcount, orig_node_free_ref); 607 orig_node_free_ref(orig_node);
608 return ret; 608 return ret;
609} 609}
610 610
@@ -730,7 +730,7 @@ void receive_bat_packet(struct ethhdr *ethhdr,
730 730
731 bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " 731 bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: "
732 "originator packet from myself (via neighbor)\n"); 732 "originator packet from myself (via neighbor)\n");
733 kref_put(&orig_neigh_node->refcount, orig_node_free_ref); 733 orig_node_free_ref(orig_neigh_node);
734 return; 734 return;
735 } 735 }
736 736
@@ -835,10 +835,10 @@ void receive_bat_packet(struct ethhdr *ethhdr,
835 0, hna_buff_len, if_incoming); 835 0, hna_buff_len, if_incoming);
836 836
837out_neigh: 837out_neigh:
838 if (!is_single_hop_neigh) 838 if ((orig_neigh_node) && (!is_single_hop_neigh))
839 kref_put(&orig_neigh_node->refcount, orig_node_free_ref); 839 orig_node_free_ref(orig_neigh_node);
840out: 840out:
841 kref_put(&orig_node->refcount, orig_node_free_ref); 841 orig_node_free_ref(orig_node);
842} 842}
843 843
844int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) 844int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if)
@@ -952,7 +952,7 @@ out:
952 if (neigh_node) 952 if (neigh_node)
953 neigh_node_free_ref(neigh_node); 953 neigh_node_free_ref(neigh_node);
954 if (orig_node) 954 if (orig_node)
955 kref_put(&orig_node->refcount, orig_node_free_ref); 955 orig_node_free_ref(orig_node);
956 return ret; 956 return ret;
957} 957}
958 958
@@ -1028,7 +1028,7 @@ out:
1028 if (neigh_node) 1028 if (neigh_node)
1029 neigh_node_free_ref(neigh_node); 1029 neigh_node_free_ref(neigh_node);
1030 if (orig_node) 1030 if (orig_node)
1031 kref_put(&orig_node->refcount, orig_node_free_ref); 1031 orig_node_free_ref(orig_node);
1032 return ret; 1032 return ret;
1033} 1033}
1034 1034
@@ -1134,7 +1134,7 @@ out:
1134 if (neigh_node) 1134 if (neigh_node)
1135 neigh_node_free_ref(neigh_node); 1135 neigh_node_free_ref(neigh_node);
1136 if (orig_node) 1136 if (orig_node)
1137 kref_put(&orig_node->refcount, orig_node_free_ref); 1137 orig_node_free_ref(orig_node);
1138 return ret; 1138 return ret;
1139} 1139}
1140 1140
@@ -1189,7 +1189,7 @@ struct neigh_node *find_router(struct bat_priv *bat_priv,
1189 if (!primary_orig_node) 1189 if (!primary_orig_node)
1190 goto return_router; 1190 goto return_router;
1191 1191
1192 kref_put(&primary_orig_node->refcount, orig_node_free_ref); 1192 orig_node_free_ref(primary_orig_node);
1193 } 1193 }
1194 1194
1195 /* with less than 2 candidates, we can't do any 1195 /* with less than 2 candidates, we can't do any
@@ -1401,7 +1401,7 @@ out:
1401 if (neigh_node) 1401 if (neigh_node)
1402 neigh_node_free_ref(neigh_node); 1402 neigh_node_free_ref(neigh_node);
1403 if (orig_node) 1403 if (orig_node)
1404 kref_put(&orig_node->refcount, orig_node_free_ref); 1404 orig_node_free_ref(orig_node);
1405 return ret; 1405 return ret;
1406} 1406}
1407 1407
@@ -1543,7 +1543,7 @@ spin_unlock:
1543 spin_unlock_bh(&bat_priv->orig_hash_lock); 1543 spin_unlock_bh(&bat_priv->orig_hash_lock);
1544out: 1544out:
1545 if (orig_node) 1545 if (orig_node)
1546 kref_put(&orig_node->refcount, orig_node_free_ref); 1546 orig_node_free_ref(orig_node);
1547 return ret; 1547 return ret;
1548} 1548}
1549 1549
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index cd8a58396d26..8d15b48d1692 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -589,17 +589,20 @@ void hna_global_free(struct bat_priv *bat_priv)
589struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) 589struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr)
590{ 590{
591 struct hna_global_entry *hna_global_entry; 591 struct hna_global_entry *hna_global_entry;
592 struct orig_node *orig_node = NULL;
592 593
593 spin_lock_bh(&bat_priv->hna_ghash_lock); 594 spin_lock_bh(&bat_priv->hna_ghash_lock);
594 hna_global_entry = hna_global_hash_find(bat_priv, addr); 595 hna_global_entry = hna_global_hash_find(bat_priv, addr);
595 596
596 if (hna_global_entry) 597 if (!hna_global_entry)
597 kref_get(&hna_global_entry->orig_node->refcount); 598 goto out;
598 599
599 spin_unlock_bh(&bat_priv->hna_ghash_lock); 600 if (!atomic_inc_not_zero(&hna_global_entry->orig_node->refcount))
601 goto out;
600 602
601 if (!hna_global_entry) 603 orig_node = hna_global_entry->orig_node;
602 return NULL;
603 604
604 return hna_global_entry->orig_node; 605out:
606 spin_unlock_bh(&bat_priv->hna_ghash_lock);
607 return orig_node;
605} 608}
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 40365b81bc40..1be76feddee1 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -84,7 +84,8 @@ struct orig_node {
84 struct hlist_head neigh_list; 84 struct hlist_head neigh_list;
85 struct list_head frag_list; 85 struct list_head frag_list;
86 spinlock_t neigh_list_lock; /* protects neighbor list */ 86 spinlock_t neigh_list_lock; /* protects neighbor list */
87 struct kref refcount; 87 atomic_t refcount;
88 struct rcu_head rcu;
88 struct hlist_node hash_entry; 89 struct hlist_node hash_entry;
89 struct bat_priv *bat_priv; 90 struct bat_priv *bat_priv;
90 unsigned long last_frag_packet; 91 unsigned long last_frag_packet;
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c
index 2d5daac52034..2ab819841231 100644
--- a/net/batman-adv/unicast.c
+++ b/net/batman-adv/unicast.c
@@ -211,7 +211,7 @@ unlock:
211 spin_unlock_bh(&bat_priv->orig_hash_lock); 211 spin_unlock_bh(&bat_priv->orig_hash_lock);
212out: 212out:
213 if (orig_node) 213 if (orig_node)
214 kref_put(&orig_node->refcount, orig_node_free_ref); 214 orig_node_free_ref(orig_node);
215 return ret; 215 return ret;
216} 216}
217 217
@@ -280,7 +280,7 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv)
280{ 280{
281 struct ethhdr *ethhdr = (struct ethhdr *)skb->data; 281 struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
282 struct unicast_packet *unicast_packet; 282 struct unicast_packet *unicast_packet;
283 struct orig_node *orig_node = NULL; 283 struct orig_node *orig_node;
284 struct batman_if *batman_if; 284 struct batman_if *batman_if;
285 struct neigh_node *neigh_node; 285 struct neigh_node *neigh_node;
286 int data_len = skb->len; 286 int data_len = skb->len;
@@ -347,7 +347,7 @@ out:
347 if (neigh_node) 347 if (neigh_node)
348 neigh_node_free_ref(neigh_node); 348 neigh_node_free_ref(neigh_node);
349 if (orig_node) 349 if (orig_node)
350 kref_put(&orig_node->refcount, orig_node_free_ref); 350 orig_node_free_ref(orig_node);
351 if (ret == 1) 351 if (ret == 1)
352 kfree_skb(skb); 352 kfree_skb(skb);
353 return ret; 353 return ret;
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index d179acabb04c..89722425dcb2 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -826,7 +826,7 @@ out:
826 if (neigh_node) 826 if (neigh_node)
827 neigh_node_free_ref(neigh_node); 827 neigh_node_free_ref(neigh_node);
828 if (orig_node) 828 if (orig_node)
829 kref_put(&orig_node->refcount, orig_node_free_ref); 829 orig_node_free_ref(orig_node);
830 return; 830 return;
831} 831}
832 832