aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2009-10-19 02:41:58 -0400
committerDavid S. Miller <davem@davemloft.net>2009-10-20 00:34:20 -0400
commit55b8050353c4a212c94d7156e2bd5885225b869b (patch)
tree013778c4d48b946b2c565f8b55f40e505ec255ce
parent45054dc1bf2367ccb0e7c0486037907cd9395f8b (diff)
net: Fix IP_MULTICAST_IF
ipv4/ipv6 setsockopt(IP_MULTICAST_IF) have dubious __dev_get_by_index() calls. This function should be called only with RTNL or dev_base_lock held, or reader could see a corrupt hash chain and eventually enter an endless loop. Fix is to call dev_get_by_index()/dev_put(). If this happens to be performance critical, we could define a new dev_exist_by_index() function to avoid touching dev refcount. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/ipv4/ip_sockglue.c7
-rw-r--r--net/ipv6/ipv6_sockglue.c6
2 files changed, 8 insertions, 5 deletions
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 0c0b6e363a20..e982b5c1ee17 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -634,17 +634,16 @@ static int do_ip_setsockopt(struct sock *sk, int level,
634 break; 634 break;
635 } 635 }
636 dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr); 636 dev = ip_dev_find(sock_net(sk), mreq.imr_address.s_addr);
637 if (dev) { 637 if (dev)
638 mreq.imr_ifindex = dev->ifindex; 638 mreq.imr_ifindex = dev->ifindex;
639 dev_put(dev);
640 }
641 } else 639 } else
642 dev = __dev_get_by_index(sock_net(sk), mreq.imr_ifindex); 640 dev = dev_get_by_index(sock_net(sk), mreq.imr_ifindex);
643 641
644 642
645 err = -EADDRNOTAVAIL; 643 err = -EADDRNOTAVAIL;
646 if (!dev) 644 if (!dev)
647 break; 645 break;
646 dev_put(dev);
648 647
649 err = -EINVAL; 648 err = -EINVAL;
650 if (sk->sk_bound_dev_if && 649 if (sk->sk_bound_dev_if &&
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 14f54eb5a7fc..4f7aaf6996a3 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -496,13 +496,17 @@ done:
496 goto e_inval; 496 goto e_inval;
497 497
498 if (val) { 498 if (val) {
499 struct net_device *dev;
500
499 if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val) 501 if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val)
500 goto e_inval; 502 goto e_inval;
501 503
502 if (__dev_get_by_index(net, val) == NULL) { 504 dev = dev_get_by_index(net, val);
505 if (!dev) {
503 retv = -ENODEV; 506 retv = -ENODEV;
504 break; 507 break;
505 } 508 }
509 dev_put(dev);
506 } 510 }
507 np->mcast_oif = val; 511 np->mcast_oif = val;
508 retv = 0; 512 retv = 0;