Skip to content

Commit

Permalink
batman-adv: Correct rcu refcounting for softif_neigh
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Marek Lindner committed Mar 5, 2011
1 parent 25b6d3c commit 7d2b554
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 17 deletions.
31 changes: 15 additions & 16 deletions net/batman-adv/soft-interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,18 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len)
return 0;
}

static void softif_neigh_free_ref(struct kref *refcount)
static void softif_neigh_free_rcu(struct rcu_head *rcu)
{
struct softif_neigh *softif_neigh;

softif_neigh = container_of(refcount, struct softif_neigh, refcount);
softif_neigh = container_of(rcu, struct softif_neigh, rcu);
kfree(softif_neigh);
}

static void softif_neigh_free_rcu(struct rcu_head *rcu)
static void softif_neigh_free_ref(struct softif_neigh *softif_neigh)
{
struct softif_neigh *softif_neigh;

softif_neigh = container_of(rcu, struct softif_neigh, rcu);
kref_put(&softif_neigh->refcount, softif_neigh_free_ref);
if (atomic_dec_and_test(&softif_neigh->refcount))
call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu);
}

void softif_neigh_purge(struct bat_priv *bat_priv)
Expand All @@ -116,11 +114,10 @@ void softif_neigh_purge(struct bat_priv *bat_priv)
softif_neigh->addr, softif_neigh->vid);
softif_neigh_tmp = bat_priv->softif_neigh;
bat_priv->softif_neigh = NULL;
kref_put(&softif_neigh_tmp->refcount,
softif_neigh_free_ref);
softif_neigh_free_ref(softif_neigh_tmp);
}

call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu);
softif_neigh_free_ref(softif_neigh);
}

spin_unlock_bh(&bat_priv->softif_neigh_lock);
Expand All @@ -141,8 +138,11 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv,
if (softif_neigh->vid != vid)
continue;

if (!atomic_inc_not_zero(&softif_neigh->refcount))
continue;

softif_neigh->last_seen = jiffies;
goto found;
goto out;
}

softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC);
Expand All @@ -152,15 +152,14 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv,
memcpy(softif_neigh->addr, addr, ETH_ALEN);
softif_neigh->vid = vid;
softif_neigh->last_seen = jiffies;
kref_init(&softif_neigh->refcount);
/* initialize with 2 - caller decrements counter by one */
atomic_set(&softif_neigh->refcount, 2);

INIT_HLIST_NODE(&softif_neigh->list);
spin_lock_bh(&bat_priv->softif_neigh_lock);
hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list);
spin_unlock_bh(&bat_priv->softif_neigh_lock);

found:
kref_get(&softif_neigh->refcount);
out:
rcu_read_unlock();
return softif_neigh;
Expand Down Expand Up @@ -264,7 +263,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
softif_neigh->addr, softif_neigh->vid);
softif_neigh_tmp = bat_priv->softif_neigh;
bat_priv->softif_neigh = softif_neigh;
kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref);
softif_neigh_free_ref(softif_neigh_tmp);
/* we need to hold the additional reference */
goto err;
}
Expand All @@ -282,7 +281,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev,
}

out:
kref_put(&softif_neigh->refcount, softif_neigh_free_ref);
softif_neigh_free_ref(softif_neigh);
err:
kfree_skb(skb);
return;
Expand Down
2 changes: 1 addition & 1 deletion net/batman-adv/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ struct softif_neigh {
uint8_t addr[ETH_ALEN];
unsigned long last_seen;
short vid;
struct kref refcount;
atomic_t refcount;
struct rcu_head rcu;
};

Expand Down

0 comments on commit 7d2b554

Please sign in to comment.