aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv/vis.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/vis.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/vis.c')
-rw-r--r--net/batman-adv/vis.c33
1 files changed, 27 insertions, 6 deletions
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index 8092eadcbdee..9832d8f9ed44 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -764,21 +764,35 @@ static void unicast_vis_packet(struct bat_priv *bat_priv,
764 struct vis_info *info) 764 struct vis_info *info)
765{ 765{
766 struct orig_node *orig_node; 766 struct orig_node *orig_node;
767 struct neigh_node *neigh_node = NULL;
767 struct sk_buff *skb; 768 struct sk_buff *skb;
768 struct vis_packet *packet; 769 struct vis_packet *packet;
769 struct batman_if *batman_if; 770 struct batman_if *batman_if;
770 uint8_t dstaddr[ETH_ALEN]; 771 uint8_t dstaddr[ETH_ALEN];
771 772
772 spin_lock_bh(&bat_priv->orig_hash_lock);
773 packet = (struct vis_packet *)info->skb_packet->data; 773 packet = (struct vis_packet *)info->skb_packet->data;
774
775 spin_lock_bh(&bat_priv->orig_hash_lock);
774 rcu_read_lock(); 776 rcu_read_lock();
775 orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, 777 orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash,
776 compare_orig, choose_orig, 778 compare_orig, choose_orig,
777 packet->target_orig)); 779 packet->target_orig));
778 rcu_read_unlock();
779 780
780 if ((!orig_node) || (!orig_node->router)) 781 if (!orig_node)
781 goto out; 782 goto unlock;
783
784 kref_get(&orig_node->refcount);
785 neigh_node = orig_node->router;
786
787 if (!neigh_node)
788 goto unlock;
789
790 if (!atomic_inc_not_zero(&neigh_node->refcount)) {
791 neigh_node = NULL;
792 goto unlock;
793 }
794
795 rcu_read_unlock();
782 796
783 /* don't lock while sending the packets ... we therefore 797 /* don't lock while sending the packets ... we therefore
784 * copy the required data before sending */ 798 * copy the required data before sending */
@@ -790,10 +804,17 @@ static void unicast_vis_packet(struct bat_priv *bat_priv,
790 if (skb) 804 if (skb)
791 send_skb_packet(skb, batman_if, dstaddr); 805 send_skb_packet(skb, batman_if, dstaddr);
792 806
793 return; 807 goto out;
794 808
795out: 809unlock:
810 rcu_read_unlock();
796 spin_unlock_bh(&bat_priv->orig_hash_lock); 811 spin_unlock_bh(&bat_priv->orig_hash_lock);
812out:
813 if (neigh_node)
814 neigh_node_free_ref(neigh_node);
815 if (orig_node)
816 kref_put(&orig_node->refcount, orig_node_free_ref);
817 return;
797} 818}
798 819
799/* only send one vis packet. called from send_vis_packets() */ 820/* only send one vis packet. called from send_vis_packets() */