]> git.hungrycats.org Git - linux/commitdiff
[IPV6]: Fix dangling multicast device references.
authorDavid S. Miller <davem@nuts.ninka.net>
Thu, 21 Aug 2003 07:50:53 +0000 (00:50 -0700)
committerDavid S. Miller <davem@nuts.ninka.net>
Thu, 21 Aug 2003 07:50:53 +0000 (00:50 -0700)
When addrconf_ifdown() calls ipv6_mc_destroy_dev(), it has NULL'd
out dev->ip6_ptr, which means all in6_dev_get() calls will fail.
So pass an explicit idev into ipv6_dev_mc_dec() in this case so
that we don't leak the all-nodes multicast address reference to the
idev.

net/ipv6/mcast.c

index 51e47c4f0ab6d82e7743116e4a0eb3f7641d979f..76d0edd929c2ad75d693a7046c8a44d06b6fac07 100644 (file)
@@ -854,15 +854,10 @@ int ipv6_dev_mc_inc(struct net_device *dev, struct in6_addr *addr)
 /*
  *     device multicast group del
  */
-int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr)
+static int __ipv6_dev_mc_dec(struct net_device *dev, struct inet6_dev *idev, struct in6_addr *addr)
 {
-       struct inet6_dev *idev;
        struct ifmcaddr6 *ma, **map;
 
-       idev = in6_dev_get(dev);
-       if (idev == NULL)
-               return -ENODEV;
-
        write_lock_bh(&idev->lock);
        for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) {
                if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) {
@@ -873,20 +868,32 @@ int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr)
                                igmp6_group_dropped(ma);
 
                                ma_put(ma);
-                               in6_dev_put(idev);
                                return 0;
                        }
                        write_unlock_bh(&idev->lock);
-                       in6_dev_put(idev);
                        return 0;
                }
        }
        write_unlock_bh(&idev->lock);
-       in6_dev_put(idev);
 
        return -ENOENT;
 }
 
+int ipv6_dev_mc_dec(struct net_device *dev, struct in6_addr *addr)
+{
+       struct inet6_dev *idev = in6_dev_get(dev);
+       int err;
+
+       if (!idev)
+               return -ENODEV;
+
+       err = __ipv6_dev_mc_dec(dev, idev, addr);
+
+       in6_dev_put(idev);
+
+       return err;
+}
+
 /*
  *     check if the interface/address pair is valid
  */
@@ -2024,7 +2031,12 @@ void ipv6_mc_destroy_dev(struct inet6_dev *idev)
 
        /* Delete all-nodes address. */
        ipv6_addr_all_nodes(&maddr);
-       ipv6_dev_mc_dec(idev->dev, &maddr);
+
+       /* We cannot call ipv6_dev_mc_dec() directly, our caller in
+        * addrconf.c has NULL'd out dev->ip6_ptr so in6_dev_get() will
+        * fail.
+        */
+       __ipv6_dev_mc_dec(idev->dev, idev, &maddr);
 
        write_lock_bh(&idev->lock);
        while ((i = idev->mc_list) != NULL) {