From patchwork Thu Sep 16 21:22:53 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sven Eckelmann X-Patchwork-Id: 420 Return-Path: Received: from mail.gmx.net (mailout-de.gmx.net [213.165.64.23]) by open-mesh.org (Postfix) with SMTP id A4B5415430E for ; Thu, 16 Sep 2010 23:22:07 +0200 (CEST) Received: (qmail invoked by alias); 16 Sep 2010 21:22:06 -0000 Received: from unknown (EHLO sven-desktop.lazhur.ath.cx) [89.246.214.232] by mail.gmx.net (mp029) with SMTP; 16 Sep 2010 23:22:06 +0200 X-Authenticated: #15668376 X-Provags-ID: V01U2FsdGVkX1/aC7xA9ZraBzP1+tIi7qqFlUn4wkCjVgwwGAIw5N DY7guH+N+xLKLH From: Sven Eckelmann To: b.a.t.m.a.n@lists.open-mesh.net Date: Thu, 16 Sep 2010 23:22:53 +0200 Message-Id: <1284672174-27464-2-git-send-email-sven.eckelmann@gmx.de> X-Mailer: git-send-email 1.7.2.3 In-Reply-To: <1284668317-19890-1-git-send-email-sven.eckelmann@gmx.de> References: <1284668317-19890-1-git-send-email-sven.eckelmann@gmx.de> X-Y-GMX-Trusted: 0 Subject: [B.A.T.M.A.N.] [PATCH 1/2] batman-adv: Use refcnt to track usage count of gw_node X-BeenThere: b.a.t.m.a.n@lists.open-mesh.org X-Mailman-Version: 2.1.11 Precedence: list Reply-To: The list for a Better Approach To Mobile Ad-hoc Networking List-Id: The list for a Better Approach To Mobile Ad-hoc Networking List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 16 Sep 2010 21:22:07 -0000 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 --- batman-adv/gateway_client.c | 19 +++++++++++++++++-- batman-adv/types.h | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/batman-adv/gateway_client.c b/batman-adv/gateway_client.c index bfac0ff..281da92 100644 --- a/batman-adv/gateway_client.c +++ b/batman-adv/gateway_client.c @@ -28,6 +28,19 @@ #include #include +static void gw_node_free(struct rcu_head *rcu); + +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)) + call_rcu(&gw_node->rcu, gw_node_free); +} + void *gw_get_selected(struct bat_priv *bat_priv) { struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; @@ -205,6 +218,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); @@ -286,7 +301,7 @@ void gw_node_purge_deleted(struct bat_priv *bat_priv) (time_after(jiffies, gw_node->deleted + timeout))) { hlist_del_rcu(&gw_node->list); - call_rcu(&gw_node->rcu, gw_node_free); + gw_node_put(gw_node); } } @@ -304,7 +319,7 @@ void gw_node_list_free(struct bat_priv *bat_priv) hlist_for_each_entry_safe(gw_node, node, node_tmp, &bat_priv->gw_list, list) { hlist_del_rcu(&gw_node->list); - call_rcu(&gw_node->rcu, gw_node_free); + gw_node_put(gw_node); } gw_deselect(bat_priv); diff --git a/batman-adv/types.h b/batman-adv/types.h index e7b53a4..a088064 100644 --- a/batman-adv/types.h +++ b/batman-adv/types.h @@ -96,6 +96,7 @@ struct gw_node { struct hlist_node list; struct orig_node *orig_node; unsigned long deleted; + atomic_t refcnt; struct rcu_head rcu; };