diff options
author | Sabrina Dubroca <sd@queasysnail.net> | 2014-09-02 04:29:29 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-05 14:52:28 -0400 |
commit | a9ed4a2986e13011fcf4ed2d1a1647c53112f55b (patch) | |
tree | 508293e1b03b96cfdfd05e57ddd8dc60c0157b1d | |
parent | 4ad9a64f53c619969dede1143d56ccda1a453c39 (diff) |
ipv6: fix rtnl locking in setsockopt for anycast and multicast
Calling setsockopt with IPV6_JOIN_ANYCAST or IPV6_LEAVE_ANYCAST
triggers the assertion in addrconf_join_solict()/addrconf_leave_solict()
ipv6_sock_ac_join(), ipv6_sock_ac_drop(), ipv6_sock_ac_close() need to
take RTNL before calling ipv6_dev_ac_inc/dec. Same thing with
ipv6_sock_mc_join(), ipv6_sock_mc_drop(), ipv6_sock_mc_close() before
calling ipv6_dev_mc_inc/dec.
This patch moves ASSERT_RTNL() up a level in the call stack.
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Reported-by: Tommi Rantala <tt.rantala@gmail.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv6/addrconf.c | 15 | ||||
-rw-r--r-- | net/ipv6/anycast.c | 12 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 14 |
3 files changed, 31 insertions, 10 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 0b239fc1816e..aa0e135b808c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -1690,14 +1690,12 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) | |||
1690 | addrconf_mod_dad_work(ifp, 0); | 1690 | addrconf_mod_dad_work(ifp, 0); |
1691 | } | 1691 | } |
1692 | 1692 | ||
1693 | /* Join to solicited addr multicast group. */ | 1693 | /* Join to solicited addr multicast group. |
1694 | 1694 | * caller must hold RTNL */ | |
1695 | void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr) | 1695 | void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr) |
1696 | { | 1696 | { |
1697 | struct in6_addr maddr; | 1697 | struct in6_addr maddr; |
1698 | 1698 | ||
1699 | ASSERT_RTNL(); | ||
1700 | |||
1701 | if (dev->flags&(IFF_LOOPBACK|IFF_NOARP)) | 1699 | if (dev->flags&(IFF_LOOPBACK|IFF_NOARP)) |
1702 | return; | 1700 | return; |
1703 | 1701 | ||
@@ -1705,12 +1703,11 @@ void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr) | |||
1705 | ipv6_dev_mc_inc(dev, &maddr); | 1703 | ipv6_dev_mc_inc(dev, &maddr); |
1706 | } | 1704 | } |
1707 | 1705 | ||
1706 | /* caller must hold RTNL */ | ||
1708 | void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr) | 1707 | void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr) |
1709 | { | 1708 | { |
1710 | struct in6_addr maddr; | 1709 | struct in6_addr maddr; |
1711 | 1710 | ||
1712 | ASSERT_RTNL(); | ||
1713 | |||
1714 | if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP)) | 1711 | if (idev->dev->flags&(IFF_LOOPBACK|IFF_NOARP)) |
1715 | return; | 1712 | return; |
1716 | 1713 | ||
@@ -1718,12 +1715,11 @@ void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr) | |||
1718 | __ipv6_dev_mc_dec(idev, &maddr); | 1715 | __ipv6_dev_mc_dec(idev, &maddr); |
1719 | } | 1716 | } |
1720 | 1717 | ||
1718 | /* caller must hold RTNL */ | ||
1721 | static void addrconf_join_anycast(struct inet6_ifaddr *ifp) | 1719 | static void addrconf_join_anycast(struct inet6_ifaddr *ifp) |
1722 | { | 1720 | { |
1723 | struct in6_addr addr; | 1721 | struct in6_addr addr; |
1724 | 1722 | ||
1725 | ASSERT_RTNL(); | ||
1726 | |||
1727 | if (ifp->prefix_len >= 127) /* RFC 6164 */ | 1723 | if (ifp->prefix_len >= 127) /* RFC 6164 */ |
1728 | return; | 1724 | return; |
1729 | ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); | 1725 | ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); |
@@ -1732,12 +1728,11 @@ static void addrconf_join_anycast(struct inet6_ifaddr *ifp) | |||
1732 | ipv6_dev_ac_inc(ifp->idev->dev, &addr); | 1728 | ipv6_dev_ac_inc(ifp->idev->dev, &addr); |
1733 | } | 1729 | } |
1734 | 1730 | ||
1731 | /* caller must hold RTNL */ | ||
1735 | static void addrconf_leave_anycast(struct inet6_ifaddr *ifp) | 1732 | static void addrconf_leave_anycast(struct inet6_ifaddr *ifp) |
1736 | { | 1733 | { |
1737 | struct in6_addr addr; | 1734 | struct in6_addr addr; |
1738 | 1735 | ||
1739 | ASSERT_RTNL(); | ||
1740 | |||
1741 | if (ifp->prefix_len >= 127) /* RFC 6164 */ | 1736 | if (ifp->prefix_len >= 127) /* RFC 6164 */ |
1742 | return; | 1737 | return; |
1743 | ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); | 1738 | ipv6_addr_prefix(&addr, &ifp->addr, ifp->prefix_len); |
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 210183244689..45b9d81d91e8 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c | |||
@@ -77,6 +77,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
77 | pac->acl_next = NULL; | 77 | pac->acl_next = NULL; |
78 | pac->acl_addr = *addr; | 78 | pac->acl_addr = *addr; |
79 | 79 | ||
80 | rtnl_lock(); | ||
80 | rcu_read_lock(); | 81 | rcu_read_lock(); |
81 | if (ifindex == 0) { | 82 | if (ifindex == 0) { |
82 | struct rt6_info *rt; | 83 | struct rt6_info *rt; |
@@ -137,6 +138,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
137 | 138 | ||
138 | error: | 139 | error: |
139 | rcu_read_unlock(); | 140 | rcu_read_unlock(); |
141 | rtnl_unlock(); | ||
140 | if (pac) | 142 | if (pac) |
141 | sock_kfree_s(sk, pac, sizeof(*pac)); | 143 | sock_kfree_s(sk, pac, sizeof(*pac)); |
142 | return err; | 144 | return err; |
@@ -171,13 +173,17 @@ int ipv6_sock_ac_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
171 | 173 | ||
172 | spin_unlock_bh(&ipv6_sk_ac_lock); | 174 | spin_unlock_bh(&ipv6_sk_ac_lock); |
173 | 175 | ||
176 | rtnl_lock(); | ||
174 | rcu_read_lock(); | 177 | rcu_read_lock(); |
175 | dev = dev_get_by_index_rcu(net, pac->acl_ifindex); | 178 | dev = dev_get_by_index_rcu(net, pac->acl_ifindex); |
176 | if (dev) | 179 | if (dev) |
177 | ipv6_dev_ac_dec(dev, &pac->acl_addr); | 180 | ipv6_dev_ac_dec(dev, &pac->acl_addr); |
178 | rcu_read_unlock(); | 181 | rcu_read_unlock(); |
182 | rtnl_unlock(); | ||
179 | 183 | ||
180 | sock_kfree_s(sk, pac, sizeof(*pac)); | 184 | sock_kfree_s(sk, pac, sizeof(*pac)); |
185 | if (!dev) | ||
186 | return -ENODEV; | ||
181 | return 0; | 187 | return 0; |
182 | } | 188 | } |
183 | 189 | ||
@@ -198,6 +204,7 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
198 | spin_unlock_bh(&ipv6_sk_ac_lock); | 204 | spin_unlock_bh(&ipv6_sk_ac_lock); |
199 | 205 | ||
200 | prev_index = 0; | 206 | prev_index = 0; |
207 | rtnl_lock(); | ||
201 | rcu_read_lock(); | 208 | rcu_read_lock(); |
202 | while (pac) { | 209 | while (pac) { |
203 | struct ipv6_ac_socklist *next = pac->acl_next; | 210 | struct ipv6_ac_socklist *next = pac->acl_next; |
@@ -212,6 +219,7 @@ void ipv6_sock_ac_close(struct sock *sk) | |||
212 | pac = next; | 219 | pac = next; |
213 | } | 220 | } |
214 | rcu_read_unlock(); | 221 | rcu_read_unlock(); |
222 | rtnl_unlock(); | ||
215 | } | 223 | } |
216 | 224 | ||
217 | static void aca_put(struct ifacaddr6 *ac) | 225 | static void aca_put(struct ifacaddr6 *ac) |
@@ -233,6 +241,8 @@ int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr) | |||
233 | struct rt6_info *rt; | 241 | struct rt6_info *rt; |
234 | int err; | 242 | int err; |
235 | 243 | ||
244 | ASSERT_RTNL(); | ||
245 | |||
236 | idev = in6_dev_get(dev); | 246 | idev = in6_dev_get(dev); |
237 | 247 | ||
238 | if (idev == NULL) | 248 | if (idev == NULL) |
@@ -302,6 +312,8 @@ int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr) | |||
302 | { | 312 | { |
303 | struct ifacaddr6 *aca, *prev_aca; | 313 | struct ifacaddr6 *aca, *prev_aca; |
304 | 314 | ||
315 | ASSERT_RTNL(); | ||
316 | |||
305 | write_lock_bh(&idev->lock); | 317 | write_lock_bh(&idev->lock); |
306 | prev_aca = NULL; | 318 | prev_aca = NULL; |
307 | for (aca = idev->ac_list; aca; aca = aca->aca_next) { | 319 | for (aca = idev->ac_list; aca; aca = aca->aca_next) { |
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 617f0958e164..a23b655a7627 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -172,6 +172,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
172 | mc_lst->next = NULL; | 172 | mc_lst->next = NULL; |
173 | mc_lst->addr = *addr; | 173 | mc_lst->addr = *addr; |
174 | 174 | ||
175 | rtnl_lock(); | ||
175 | rcu_read_lock(); | 176 | rcu_read_lock(); |
176 | if (ifindex == 0) { | 177 | if (ifindex == 0) { |
177 | struct rt6_info *rt; | 178 | struct rt6_info *rt; |
@@ -185,6 +186,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
185 | 186 | ||
186 | if (dev == NULL) { | 187 | if (dev == NULL) { |
187 | rcu_read_unlock(); | 188 | rcu_read_unlock(); |
189 | rtnl_unlock(); | ||
188 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 190 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
189 | return -ENODEV; | 191 | return -ENODEV; |
190 | } | 192 | } |
@@ -202,6 +204,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
202 | 204 | ||
203 | if (err) { | 205 | if (err) { |
204 | rcu_read_unlock(); | 206 | rcu_read_unlock(); |
207 | rtnl_unlock(); | ||
205 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); | 208 | sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); |
206 | return err; | 209 | return err; |
207 | } | 210 | } |
@@ -212,6 +215,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
212 | spin_unlock(&ipv6_sk_mc_lock); | 215 | spin_unlock(&ipv6_sk_mc_lock); |
213 | 216 | ||
214 | rcu_read_unlock(); | 217 | rcu_read_unlock(); |
218 | rtnl_unlock(); | ||
215 | 219 | ||
216 | return 0; | 220 | return 0; |
217 | } | 221 | } |
@@ -229,6 +233,7 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
229 | if (!ipv6_addr_is_multicast(addr)) | 233 | if (!ipv6_addr_is_multicast(addr)) |
230 | return -EINVAL; | 234 | return -EINVAL; |
231 | 235 | ||
236 | rtnl_lock(); | ||
232 | spin_lock(&ipv6_sk_mc_lock); | 237 | spin_lock(&ipv6_sk_mc_lock); |
233 | for (lnk = &np->ipv6_mc_list; | 238 | for (lnk = &np->ipv6_mc_list; |
234 | (mc_lst = rcu_dereference_protected(*lnk, | 239 | (mc_lst = rcu_dereference_protected(*lnk, |
@@ -252,12 +257,15 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr) | |||
252 | } else | 257 | } else |
253 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); | 258 | (void) ip6_mc_leave_src(sk, mc_lst, NULL); |
254 | rcu_read_unlock(); | 259 | rcu_read_unlock(); |
260 | rtnl_unlock(); | ||
261 | |||
255 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); | 262 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); |
256 | kfree_rcu(mc_lst, rcu); | 263 | kfree_rcu(mc_lst, rcu); |
257 | return 0; | 264 | return 0; |
258 | } | 265 | } |
259 | } | 266 | } |
260 | spin_unlock(&ipv6_sk_mc_lock); | 267 | spin_unlock(&ipv6_sk_mc_lock); |
268 | rtnl_unlock(); | ||
261 | 269 | ||
262 | return -EADDRNOTAVAIL; | 270 | return -EADDRNOTAVAIL; |
263 | } | 271 | } |
@@ -302,6 +310,7 @@ void ipv6_sock_mc_close(struct sock *sk) | |||
302 | if (!rcu_access_pointer(np->ipv6_mc_list)) | 310 | if (!rcu_access_pointer(np->ipv6_mc_list)) |
303 | return; | 311 | return; |
304 | 312 | ||
313 | rtnl_lock(); | ||
305 | spin_lock(&ipv6_sk_mc_lock); | 314 | spin_lock(&ipv6_sk_mc_lock); |
306 | while ((mc_lst = rcu_dereference_protected(np->ipv6_mc_list, | 315 | while ((mc_lst = rcu_dereference_protected(np->ipv6_mc_list, |
307 | lockdep_is_held(&ipv6_sk_mc_lock))) != NULL) { | 316 | lockdep_is_held(&ipv6_sk_mc_lock))) != NULL) { |
@@ -328,6 +337,7 @@ void ipv6_sock_mc_close(struct sock *sk) | |||
328 | spin_lock(&ipv6_sk_mc_lock); | 337 | spin_lock(&ipv6_sk_mc_lock); |
329 | } | 338 | } |
330 | spin_unlock(&ipv6_sk_mc_lock); | 339 | spin_unlock(&ipv6_sk_mc_lock); |
340 | rtnl_unlock(); | ||
331 | } | 341 | } |
332 | 342 | ||
333 | int ip6_mc_source(int add, int omode, struct sock *sk, | 343 | int ip6_mc_source(int add, int omode, struct sock *sk, |
@@ -845,6 +855,8 @@ int ipv6_dev_mc_inc(struct net_device *dev, const struct in6_addr *addr) | |||
845 | struct ifmcaddr6 *mc; | 855 | struct ifmcaddr6 *mc; |
846 | struct inet6_dev *idev; | 856 | struct inet6_dev *idev; |
847 | 857 | ||
858 | ASSERT_RTNL(); | ||
859 | |||
848 | /* we need to take a reference on idev */ | 860 | /* we need to take a reference on idev */ |
849 | idev = in6_dev_get(dev); | 861 | idev = in6_dev_get(dev); |
850 | 862 | ||
@@ -916,6 +928,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr) | |||
916 | { | 928 | { |
917 | struct ifmcaddr6 *ma, **map; | 929 | struct ifmcaddr6 *ma, **map; |
918 | 930 | ||
931 | ASSERT_RTNL(); | ||
932 | |||
919 | write_lock_bh(&idev->lock); | 933 | write_lock_bh(&idev->lock); |
920 | for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) { | 934 | for (map = &idev->mc_list; (ma=*map) != NULL; map = &ma->next) { |
921 | if (ipv6_addr_equal(&ma->mca_addr, addr)) { | 935 | if (ipv6_addr_equal(&ma->mca_addr, addr)) { |