[commits] batman-adv; branch, master, updated. v2010.1.0-169-gaecf54d

postmaster at open-mesh.org postmaster at open-mesh.org
Sat Sep 18 17:35:48 CEST 2010


The following commit has been merged in the master branch:
commit aecf54d3a00d5b2155f59468dbcb42dc950e74c5
Author: Sven Eckelmann <sven.eckelmann at gmx.de>
Date:   Sat Sep 18 15:35:41 2010 +0000

    batman-adv: Use refcnt to track usage count of gw_node
    
    gw_election may leak data from the rcu protected list of all gateway
    nodes outside the read-side critical area. This is not valid as we may
    free the data using a call_rcu created callback after we unlock using
    rcu_read_unlock. A workaround is to provide a reference count to be sure
    that the memory isn't freed to early.
    
    It is currently only to implement the already existing functionality and
    doesn't provide the full tracking of all usage cases.
    
    Additionally, we must gw_node_hold inside the
    rcu_read_lock()..rcu_read_unlock() before we attach to the structure
    which "leaks" it. When another function now removed it from its usage
    context (curr_gw, usage on stack, ...) then we must gw_node_put it. If
    it is decremented to zero then we can issue the call_rcu to the freeing
    function. So "put" is not allowed inside an rcu_read_lock.
    
    Signed-off-by: Sven Eckelmann <sven.eckelmann at gmx.de>

diff --git a/gateway_client.c b/gateway_client.c
index 8bc1cb0..16f0757 100644
--- a/gateway_client.c
+++ b/gateway_client.c
@@ -28,6 +28,17 @@
 #include <linux/udp.h>
 #include <linux/if_vlan.h>
 
+static void gw_node_hold(struct gw_node *gw_node)
+{
+	atomic_inc(&gw_node->refcnt);
+}
+
+static void gw_node_put(struct gw_node *gw_node)
+{
+	if (atomic_dec_and_test(&gw_node->refcnt))
+		kfree(gw_node);
+}
+
 void *gw_get_selected(struct bat_priv *bat_priv)
 {
 	struct gw_node *curr_gateway_tmp = bat_priv->curr_gw;
@@ -205,6 +216,8 @@ static void gw_node_add(struct bat_priv *bat_priv,
 	memset(gw_node, 0, sizeof(struct gw_node));
 	INIT_HLIST_NODE(&gw_node->list);
 	gw_node->orig_node = orig_node;
+	atomic_set(&gw_node->refcnt, 0);
+	gw_node_hold(gw_node);
 
 	spin_lock_irqsave(&bat_priv->gw_list_lock, flags);
 	hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list);
@@ -281,7 +294,7 @@ void gw_node_purge_deleted(struct bat_priv *bat_priv)
 
 			hlist_del_rcu(&gw_node->list);
 			synchronize_rcu();
-			kfree(gw_node);
+			gw_node_put(gw_node);
 		}
 	}
 
@@ -300,7 +313,7 @@ void gw_node_list_free(struct bat_priv *bat_priv)
 				 &bat_priv->gw_list, list) {
 		hlist_del_rcu(&gw_node->list);
 		synchronize_rcu();
-		kfree(gw_node);
+		gw_node_put(gw_node);
 	}
 
 	gw_deselect(bat_priv);
diff --git a/types.h b/types.h
index 1940404..ecc4365 100644
--- a/types.h
+++ b/types.h
@@ -95,6 +95,7 @@ struct gw_node {
 	struct hlist_node list;
 	struct orig_node *orig_node;
 	unsigned long deleted;
+	atomic_t refcnt;
 };
 
 /**

-- 
batman-adv


More information about the commits mailing list