aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv/icmp_socket.c
diff options
context:
space:
mode:
authorMarek Lindner <lindner_marek@yahoo.de>2011-02-10 09:33:53 -0500
committerMarek Lindner <lindner_marek@yahoo.de>2011-03-05 06:50:03 -0500
commit44524fcdf6ca19b58c24f7622c4af1d8d8fe59f8 (patch)
tree297c76f80d68d56e3c65a23c70de645a1c93df47 /net/batman-adv/icmp_socket.c
parenta4c135c561106c397bae33455acfca4aa8065a30 (diff)
batman-adv: Correct rcu refcounting for neigh_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/icmp_socket.c')
-rw-r--r--net/batman-adv/icmp_socket.c27
1 files changed, 20 insertions, 7 deletions
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index 8e0cd8a1bc02..7fa5bb8a9409 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -156,7 +156,8 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
156 struct sk_buff *skb; 156 struct sk_buff *skb;
157 struct icmp_packet_rr *icmp_packet; 157 struct icmp_packet_rr *icmp_packet;
158 158
159 struct orig_node *orig_node; 159 struct orig_node *orig_node = NULL;
160 struct neigh_node *neigh_node = NULL;
160 struct batman_if *batman_if; 161 struct batman_if *batman_if;
161 size_t packet_len = sizeof(struct icmp_packet); 162 size_t packet_len = sizeof(struct icmp_packet);
162 uint8_t dstaddr[ETH_ALEN]; 163 uint8_t dstaddr[ETH_ALEN];
@@ -224,17 +225,25 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
224 orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, 225 orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
225 compare_orig, choose_orig, 226 compare_orig, choose_orig,
226 icmp_packet->dst)); 227 icmp_packet->dst));
227 rcu_read_unlock();
228 228
229 if (!orig_node) 229 if (!orig_node)
230 goto unlock; 230 goto unlock;
231 231
232 if (!orig_node->router) 232 kref_get(&orig_node->refcount);
233 neigh_node = orig_node->router;
234
235 if (!neigh_node)
236 goto unlock;
237
238 if (!atomic_inc_not_zero(&neigh_node->refcount)) {
239 neigh_node = NULL;
233 goto unlock; 240 goto unlock;
241 }
242
243 rcu_read_unlock();
234 244
235 batman_if = orig_node->router->if_incoming; 245 batman_if = orig_node->router->if_incoming;
236 memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); 246 memcpy(dstaddr, orig_node->router->addr, ETH_ALEN);
237
238 spin_unlock_bh(&bat_priv->orig_hash_lock); 247 spin_unlock_bh(&bat_priv->orig_hash_lock);
239 248
240 if (!batman_if) 249 if (!batman_if)
@@ -247,14 +256,14 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff,
247 bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); 256 bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
248 257
249 if (packet_len == sizeof(struct icmp_packet_rr)) 258 if (packet_len == sizeof(struct icmp_packet_rr))
250 memcpy(icmp_packet->rr, batman_if->net_dev->dev_addr, ETH_ALEN); 259 memcpy(icmp_packet->rr,
251 260 batman_if->net_dev->dev_addr, ETH_ALEN);
252 261
253 send_skb_packet(skb, batman_if, dstaddr); 262 send_skb_packet(skb, batman_if, dstaddr);
254
255 goto out; 263 goto out;
256 264
257unlock: 265unlock:
266 rcu_read_unlock();
258 spin_unlock_bh(&bat_priv->orig_hash_lock); 267 spin_unlock_bh(&bat_priv->orig_hash_lock);
259dst_unreach: 268dst_unreach:
260 icmp_packet->msg_type = DESTINATION_UNREACHABLE; 269 icmp_packet->msg_type = DESTINATION_UNREACHABLE;
@@ -262,6 +271,10 @@ dst_unreach:
262free_skb: 271free_skb:
263 kfree_skb(skb); 272 kfree_skb(skb);
264out: 273out:
274 if (neigh_node)
275 neigh_node_free_ref(neigh_node);
276 if (orig_node)
277 kref_put(&orig_node->refcount, orig_node_free_ref);
265 return len; 278 return len;
266} 279}
267 280