ipv6: fix null pointer dereference in __ip6addrlbl_add
authorHannes Frederic Sowa <hannes@stressinduktion.org>
Tue, 3 Sep 2013 00:13:31 +0000 (02:13 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 4 Sep 2013 18:14:53 +0000 (14:14 -0400)
Commit b67bfe0d42cac56c512dd5da4b1b347a23f4b70a ("hlist: drop
the node parameter from iterators") changed the behavior of
hlist_for_each_entry_safe to leave the p argument NULL.

Fix this up by tracking the last argument.

Reported-by: Michele Baldessari <michele@acksyn.org>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Tested-by: Michele Baldessari <michele@acksyn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/addrlabel.c

index f083a583a05cfac719a1df3fd4dc0e58e30f431f..b30ad3741b46732ee2c38bc851fa759ac02ff698 100644 (file)
@@ -251,38 +251,36 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net,
 /* add a label */
 static int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace)
 {
+       struct hlist_node *n;
+       struct ip6addrlbl_entry *last = NULL, *p = NULL;
        int ret = 0;
 
-       ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n",
-                       __func__,
-                       newp, replace);
+       ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp,
+                 replace);
 
-       if (hlist_empty(&ip6addrlbl_table.head)) {
-               hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head);
-       } else {
-               struct hlist_node *n;
-               struct ip6addrlbl_entry *p = NULL;
-               hlist_for_each_entry_safe(p, n,
-                                         &ip6addrlbl_table.head, list) {
-                       if (p->prefixlen == newp->prefixlen &&
-                           net_eq(ip6addrlbl_net(p), ip6addrlbl_net(newp)) &&
-                           p->ifindex == newp->ifindex &&
-                           ipv6_addr_equal(&p->prefix, &newp->prefix)) {
-                               if (!replace) {
-                                       ret = -EEXIST;
-                                       goto out;
-                               }
-                               hlist_replace_rcu(&p->list, &newp->list);
-                               ip6addrlbl_put(p);
-                               goto out;
-                       } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
-                                  (p->prefixlen < newp->prefixlen)) {
-                               hlist_add_before_rcu(&newp->list, &p->list);
+       hlist_for_each_entry_safe(p, n, &ip6addrlbl_table.head, list) {
+               if (p->prefixlen == newp->prefixlen &&
+                   net_eq(ip6addrlbl_net(p), ip6addrlbl_net(newp)) &&
+                   p->ifindex == newp->ifindex &&
+                   ipv6_addr_equal(&p->prefix, &newp->prefix)) {
+                       if (!replace) {
+                               ret = -EEXIST;
                                goto out;
                        }
+                       hlist_replace_rcu(&p->list, &newp->list);
+                       ip6addrlbl_put(p);
+                       goto out;
+               } else if ((p->prefixlen == newp->prefixlen && !p->ifindex) ||
+                          (p->prefixlen < newp->prefixlen)) {
+                       hlist_add_before_rcu(&newp->list, &p->list);
+                       goto out;
                }
-               hlist_add_after_rcu(&p->list, &newp->list);
+               last = p;
        }
+       if (last)
+               hlist_add_after_rcu(&last->list, &newp->list);
+       else
+               hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head);
 out:
        if (!ret)
                ip6addrlbl_table.seq++;