79 lines
3.0 KiB
Diff
79 lines
3.0 KiB
Diff
From: Sven Eckelmann <sven@narfation.org>
|
|
Date: Thu, 6 Sep 2018 14:35:24 +0200
|
|
Subject: batman-adv: Prevent duplicated gateway_node entry
|
|
|
|
The function batadv_gw_node_add is responsible for adding new gw_node to
|
|
the gateway_list. It is expecting that the caller already checked that
|
|
there is not already an entry with the same key or not.
|
|
|
|
But the lock for the list is only held when the list is really modified.
|
|
This could lead to duplicated entries because another context could create
|
|
an entry with the same key between the check and the list manipulation.
|
|
|
|
The check and the manipulation of the list must therefore be in the same
|
|
locked code section.
|
|
|
|
Fixes: bc3538cabac5 ("batman-adv: adding gateway functionality")
|
|
Signed-off-by: Sven Eckelmann <sven@narfation.org>
|
|
Acked-by: Marek Lindner <mareklindner@neomailbox.ch>
|
|
|
|
Origin: upstream, https://git.open-mesh.org/batman-adv.git/commit/69b3ca714eba608fe79a51ccd89ce7050ee0b770
|
|
|
|
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
|
|
index 8b198ee798c910b40997ed9ca867fc931c53dcc3..140c61a3f1ecfec4fe23c5ddca19e18e2e86fd56 100644
|
|
--- a/net/batman-adv/gateway_client.c
|
|
+++ b/net/batman-adv/gateway_client.c
|
|
@@ -32,6 +32,7 @@
|
|
#include <linux/kernel.h>
|
|
#include <linux/kref.h>
|
|
#include <linux/list.h>
|
|
+#include <linux/lockdep.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/rculist.h>
|
|
@@ -348,6 +349,9 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv,
|
|
* @bat_priv: the bat priv with all the soft interface information
|
|
* @orig_node: originator announcing gateway capabilities
|
|
* @gateway: announced bandwidth information
|
|
+ *
|
|
+ * Has to be called with the appropriate locks being acquired
|
|
+ * (gw.list_lock).
|
|
*/
|
|
static void batadv_gw_node_add(struct batadv_priv *bat_priv,
|
|
struct batadv_orig_node *orig_node,
|
|
@@ -355,6 +359,8 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
|
|
{
|
|
struct batadv_gw_node *gw_node;
|
|
|
|
+ lockdep_assert_held(&bat_priv->gw.list_lock);
|
|
+
|
|
if (gateway->bandwidth_down == 0)
|
|
return;
|
|
|
|
@@ -369,10 +375,8 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv,
|
|
gw_node->bandwidth_down = ntohl(gateway->bandwidth_down);
|
|
gw_node->bandwidth_up = ntohl(gateway->bandwidth_up);
|
|
|
|
- spin_lock_bh(&bat_priv->gw.list_lock);
|
|
kref_get(&gw_node->refcount);
|
|
hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.gateway_list);
|
|
- spin_unlock_bh(&bat_priv->gw.list_lock);
|
|
|
|
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
|
|
"Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n",
|
|
@@ -428,11 +432,14 @@ void batadv_gw_node_update(struct batadv_priv *bat_priv,
|
|
{
|
|
struct batadv_gw_node *gw_node, *curr_gw = NULL;
|
|
|
|
+ spin_lock_bh(&bat_priv->gw.list_lock);
|
|
gw_node = batadv_gw_node_get(bat_priv, orig_node);
|
|
if (!gw_node) {
|
|
batadv_gw_node_add(bat_priv, orig_node, gateway);
|
|
+ spin_unlock_bh(&bat_priv->gw.list_lock);
|
|
goto out;
|
|
}
|
|
+ spin_unlock_bh(&bat_priv->gw.list_lock);
|
|
|
|
if (gw_node->bandwidth_down == ntohl(gateway->bandwidth_down) &&
|
|
gw_node->bandwidth_up == ntohl(gateway->bandwidth_up))
|