Skip to contentSkip to conversation

batman-adv: avoid potential race condition when adding a new neighbour

PATCH Superseded Antonio Quartulli 2 participants 3 messages Jan 27, 2014 – Jan 29, 2014

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 batman-adv: avoid potential race condition when adding a new neighbour

From: Antonio Quartulli <[email protected]>

When adding a new neighbour it is important to atomically perform the following:
- check if the neighbour already exists
- append the neighbour to the proper list

If the two operations are not performed in an atomic context it is possible that two concurrent insertions add the same neighbour twice.

---

Change from v2:
- fix typ0

 bat_iv_ogm.c | 22 ++++++++++++++++------
 originator.c | 36 ++++++++++++++++++++++++++++++++++++
 originator.h |  4 ++++
 3 files changed, 56 insertions(+), 6 deletions(-)
bat_iv_ogm.c +16 -6
index 512159b..c450fb8 100644
@@ -266,7 +266,7 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
 			struct batadv_orig_node *orig_neigh)
 {
 	struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
-	struct batadv_neigh_node *neigh_node;
+	struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node;
 
 	neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node);
 	if (!neigh_node)
@@ -281,14 +281,24 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
 	neigh_node->orig_node = orig_neigh;
 	neigh_node->if_incoming = hard_iface;
 
-	batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
-		   "Creating new neighbor %pM for orig_node %pM on interface %s\n",
-		   neigh_addr, orig_node->orig, hard_iface->net_dev->name);
-
 	spin_lock_bh(&orig_node->neigh_list_lock);
-	hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
+	tmp_neigh_node = batadv_neigh_node_get(orig_node, hard_iface,
+					       neigh_addr);
+	if (!tmp_neigh_node) {
+		hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list);
+	} else {
+		kfree(neigh_node);
+		batadv_hardif_free_ref(hard_iface);
+		neigh_node = tmp_neigh_node;
+	}
 	spin_unlock_bh(&orig_node->neigh_list_lock);
 
+	if (!tmp_neigh_node)
+		batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+			   "Creating new neighbor %pM for orig_node %pM on interface %s\n",
+			   neigh_addr, orig_node->orig,
+			   hard_iface->net_dev->name);
+
 out:
 	return neigh_node;
 }
originator.c +36 -0
index 6df12a2..8539416 100644
@@ -458,6 +458,42 @@ out:
 }
 
 /**
+ * batadv_neigh_node_get - retrieve a neighbour from the list
+ * @orig_node: originator which the neighbour belongs to
+ * @hard_iface: the interface where this neighbour is connected to
+ * @addr: the address of the neighbour
+ *
+ * Looks for and possibly returns a neighbour belonging to this originator list
+ * which is connected through the provided hard interface.
+ * Returns NULL if the neighbour is not found.
+ */
+struct batadv_neigh_node *
+batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
+		      const struct batadv_hard_iface *hard_iface,
+		      const uint8_t *addr)
+{
+	struct batadv_neigh_node *tmp_neigh_node, *res = NULL;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(tmp_neigh_node, &orig_node->neigh_list, list) {
+		if (!batadv_compare_eth(tmp_neigh_node->addr, addr))
+			continue;
+
+		if (tmp_neigh_node->if_incoming != hard_iface)
+			continue;
+
+		if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
+			continue;
+
+		res = tmp_neigh_node;
+		break;
+	}
+	rcu_read_unlock();
+
+	return res;
+}
+
+/**
  * batadv_orig_ifinfo_free_rcu - free the orig_ifinfo object
  * @rcu: rcu pointer of the orig_ifinfo object
  */
originator.h +4 -0
index 37be290..db3a9ed 100644
@@ -29,6 +29,10 @@ void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
 struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv,
 					      const uint8_t *addr);
 struct batadv_neigh_node *
+batadv_neigh_node_get(const struct batadv_orig_node *orig_node,
+		      const struct batadv_hard_iface *hard_iface,
+		      const uint8_t *addr);
+struct batadv_neigh_node *
 batadv_neigh_node_new(struct batadv_hard_iface *hard_iface,
 		      const uint8_t *neigh_addr,
 		      struct batadv_orig_node *orig_node);
Signature · 1 line
1.8.5.3
#1 lore raw

bat_iv_ogm.c batadv_iv_ogm_neigh_new(struct batadv_hard_iface · line 272

Quoted 9 lines · On Monday 27 January 2014 16:27:17 Antonio Quartulli wrote:
bat_iv_ogm.c +1 -1
@@ -266,7 +266,7 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface
 *hard_iface, struct batadv_orig_node *orig_neigh)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
-       struct batadv_neigh_node *neigh_node;
+       struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node;
 
        neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr,
 orig_node);

I don't quite get why neigh_node has to be initialized if we set a value in the next line.

Cheers,
Marek

#2 lore raw
Quoted 8 lines · Marek Lindner
Quoted 7 lines · On Monday 27 January 2014 16:27:17 Antonio Quartulli wrote:
bat_iv_ogm.c +1 -1
@@ -266,7 +266,7 @@ batadv_iv_ogm_neigh_new(struct batadv_hard_iface
 *hard_iface, struct batadv_orig_node *orig_neigh)
 {
        struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
-       struct batadv_neigh_node *neigh_node;
+       struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node;
 
Quoted 5 lines

Review batadv_iv_ogm_neigh_new(struct batadv_hard_iface · line 272

Quoted 2 lines
(quoted diff) +0 -0
        neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr,
 orig_node);

I don't quite get why neigh_node has to be initialized if we set a value in the next line.

nice catch :)
That's probably a residual from a previous approach I was trying out.

I'll send v4 soon.

Cheers,

Signature · 1 line
Antonio Quartulli