Skip to content

Commit

Permalink
[IPV6] ADDRLABEL: Fix double free on label deletion.
Browse files Browse the repository at this point in the history
If an entry is being deleted because it has only one reference, 
we immediately delete it and blindly register the rcu handler for it,
This results in oops by double freeing that object.

This patch fixes it by consolidating the code paths for the deletion;
let its rcu handler delete the object if it has no more reference.

Bug was found by Mitsuru Chinen <mitch@linux.vnet.ibm.com>

Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
YOSHIFUJI Hideaki authored and David S. Miller committed Jan 28, 2008
1 parent 3c582b3 commit 85040bc
Showing 1 changed file with 6 additions and 8 deletions.
14 changes: 6 additions & 8 deletions net/ipv6/addrlabel.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ static inline void ip6addrlbl_free(struct ip6addrlbl_entry *p)
kfree(p);
}

static void ip6addrlbl_free_rcu(struct rcu_head *h)
{
ip6addrlbl_free(container_of(h, struct ip6addrlbl_entry, rcu));
}

static inline int ip6addrlbl_hold(struct ip6addrlbl_entry *p)
{
return atomic_inc_not_zero(&p->refcnt);
Expand All @@ -114,12 +119,7 @@ static inline int ip6addrlbl_hold(struct ip6addrlbl_entry *p)
static inline void ip6addrlbl_put(struct ip6addrlbl_entry *p)
{
if (atomic_dec_and_test(&p->refcnt))
ip6addrlbl_free(p);
}

static void ip6addrlbl_free_rcu(struct rcu_head *h)
{
ip6addrlbl_free(container_of(h, struct ip6addrlbl_entry, rcu));
call_rcu(&p->rcu, ip6addrlbl_free_rcu);
}

/* Find label */
Expand Down Expand Up @@ -240,7 +240,6 @@ static int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace)
}
hlist_replace_rcu(&p->list, &newp->list);
ip6addrlbl_put(p);
call_rcu(&p->rcu, ip6addrlbl_free_rcu);
goto out;
} else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
(p->prefixlen < newp->prefixlen)) {
Expand Down Expand Up @@ -300,7 +299,6 @@ static int __ip6addrlbl_del(const struct in6_addr *prefix, int prefixlen,
ipv6_addr_equal(&p->prefix, prefix)) {
hlist_del_rcu(&p->list);
ip6addrlbl_put(p);
call_rcu(&p->rcu, ip6addrlbl_free_rcu);
ret = 0;
break;
}
Expand Down

0 comments on commit 85040bc

Please sign in to comment.