aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorJiri Benc <jbenc@redhat.com>2013-08-01 04:41:28 -0400
committerDavid S. Miller <davem@davemloft.net>2013-08-01 17:16:20 -0400
commit8a226b2cfa776db6011fc84b71578513161cd3d3 (patch)
tree7d9cc6e14f0e99e576f761f82c1d350378ef44e8 /net/ipv6
parent3f8f52982ad020f0704548c46de66bf464d3b967 (diff)
ipv6: prevent race between address creation and removal
There's a race in IPv6 automatic addess assignment. The address is created with zero lifetime when it's added to various address lists. Before it gets assigned the correct lifetime, there's a window where a new address may be configured. This causes the semi-initiated address to be deleted in addrconf_verify. This was discovered as a reference leak caused by concurrent run of __ipv6_ifa_notify for both RTM_NEWADDR and RTM_DELADDR with the same address. Fix this by setting the lifetime before the address is added to inet6_addr_lst. A few notes: 1. In addrconf_prefix_rcv, by setting update_lft to zero, the if (update_lft) { ... } condition is no longer executed for newly created addresses. This is okay, as the ifp fields are set in ipv6_add_addr now and ipv6_ifa_notify is called (and has been called) through addrconf_dad_start. 2. The removal of the whole block under ifp->lock in inet6_addr_add is okay, too, as tstamp is initialized to jiffies in ipv6_add_addr. Signed-off-by: Jiri Benc <jbenc@redhat.com> Signed-off-by: Jiri Pirko <jiri@resnulli.us> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/addrconf.c31
1 files changed, 15 insertions, 16 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index a0ce957fb671..da4241c8c7da 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -815,7 +815,7 @@ static u32 inet6_addr_hash(const struct in6_addr *addr)
815static struct inet6_ifaddr * 815static struct inet6_ifaddr *
816ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, 816ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
817 const struct in6_addr *peer_addr, int pfxlen, 817 const struct in6_addr *peer_addr, int pfxlen,
818 int scope, u32 flags) 818 int scope, u32 flags, u32 valid_lft, u32 prefered_lft)
819{ 819{
820 struct inet6_ifaddr *ifa = NULL; 820 struct inet6_ifaddr *ifa = NULL;
821 struct rt6_info *rt; 821 struct rt6_info *rt;
@@ -875,6 +875,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
875 ifa->scope = scope; 875 ifa->scope = scope;
876 ifa->prefix_len = pfxlen; 876 ifa->prefix_len = pfxlen;
877 ifa->flags = flags | IFA_F_TENTATIVE; 877 ifa->flags = flags | IFA_F_TENTATIVE;
878 ifa->valid_lft = valid_lft;
879 ifa->prefered_lft = prefered_lft;
878 ifa->cstamp = ifa->tstamp = jiffies; 880 ifa->cstamp = ifa->tstamp = jiffies;
879 ifa->tokenized = false; 881 ifa->tokenized = false;
880 882
@@ -1127,7 +1129,8 @@ retry:
1127 ift = !max_addresses || 1129 ift = !max_addresses ||
1128 ipv6_count_addresses(idev) < max_addresses ? 1130 ipv6_count_addresses(idev) < max_addresses ?
1129 ipv6_add_addr(idev, &addr, NULL, tmp_plen, 1131 ipv6_add_addr(idev, &addr, NULL, tmp_plen,
1130 ipv6_addr_scope(&addr), addr_flags) : NULL; 1132 ipv6_addr_scope(&addr), addr_flags,
1133 tmp_valid_lft, tmp_prefered_lft) : NULL;
1131 if (IS_ERR_OR_NULL(ift)) { 1134 if (IS_ERR_OR_NULL(ift)) {
1132 in6_ifa_put(ifp); 1135 in6_ifa_put(ifp);
1133 in6_dev_put(idev); 1136 in6_dev_put(idev);
@@ -1139,8 +1142,6 @@ retry:
1139 1142
1140 spin_lock_bh(&ift->lock); 1143 spin_lock_bh(&ift->lock);
1141 ift->ifpub = ifp; 1144 ift->ifpub = ifp;
1142 ift->valid_lft = tmp_valid_lft;
1143 ift->prefered_lft = tmp_prefered_lft;
1144 ift->cstamp = now; 1145 ift->cstamp = now;
1145 ift->tstamp = tmp_tstamp; 1146 ift->tstamp = tmp_tstamp;
1146 spin_unlock_bh(&ift->lock); 1147 spin_unlock_bh(&ift->lock);
@@ -2185,14 +2186,16 @@ ok:
2185 ifp = ipv6_add_addr(in6_dev, &addr, NULL, 2186 ifp = ipv6_add_addr(in6_dev, &addr, NULL,
2186 pinfo->prefix_len, 2187 pinfo->prefix_len,
2187 addr_type&IPV6_ADDR_SCOPE_MASK, 2188 addr_type&IPV6_ADDR_SCOPE_MASK,
2188 addr_flags); 2189 addr_flags, valid_lft,
2190 prefered_lft);
2189 2191
2190 if (IS_ERR_OR_NULL(ifp)) { 2192 if (IS_ERR_OR_NULL(ifp)) {
2191 in6_dev_put(in6_dev); 2193 in6_dev_put(in6_dev);
2192 return; 2194 return;
2193 } 2195 }
2194 2196
2195 update_lft = create = 1; 2197 update_lft = 0;
2198 create = 1;
2196 ifp->cstamp = jiffies; 2199 ifp->cstamp = jiffies;
2197 ifp->tokenized = tokenized; 2200 ifp->tokenized = tokenized;
2198 addrconf_dad_start(ifp); 2201 addrconf_dad_start(ifp);
@@ -2213,7 +2216,7 @@ ok:
2213 stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ; 2216 stored_lft = ifp->valid_lft - (now - ifp->tstamp) / HZ;
2214 else 2217 else
2215 stored_lft = 0; 2218 stored_lft = 0;
2216 if (!update_lft && stored_lft) { 2219 if (!update_lft && !create && stored_lft) {
2217 if (valid_lft > MIN_VALID_LIFETIME || 2220 if (valid_lft > MIN_VALID_LIFETIME ||
2218 valid_lft > stored_lft) 2221 valid_lft > stored_lft)
2219 update_lft = 1; 2222 update_lft = 1;
@@ -2459,15 +2462,10 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
2459 prefered_lft = timeout; 2462 prefered_lft = timeout;
2460 } 2463 }
2461 2464
2462 ifp = ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags); 2465 ifp = ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags,
2466 valid_lft, prefered_lft);
2463 2467
2464 if (!IS_ERR(ifp)) { 2468 if (!IS_ERR(ifp)) {
2465 spin_lock_bh(&ifp->lock);
2466 ifp->valid_lft = valid_lft;
2467 ifp->prefered_lft = prefered_lft;
2468 ifp->tstamp = jiffies;
2469 spin_unlock_bh(&ifp->lock);
2470
2471 addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 2469 addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev,
2472 expires, flags); 2470 expires, flags);
2473 /* 2471 /*
@@ -2559,7 +2557,8 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
2559{ 2557{
2560 struct inet6_ifaddr *ifp; 2558 struct inet6_ifaddr *ifp;
2561 2559
2562 ifp = ipv6_add_addr(idev, addr, NULL, plen, scope, IFA_F_PERMANENT); 2560 ifp = ipv6_add_addr(idev, addr, NULL, plen,
2561 scope, IFA_F_PERMANENT, 0, 0);
2563 if (!IS_ERR(ifp)) { 2562 if (!IS_ERR(ifp)) {
2564 spin_lock_bh(&ifp->lock); 2563 spin_lock_bh(&ifp->lock);
2565 ifp->flags &= ~IFA_F_TENTATIVE; 2564 ifp->flags &= ~IFA_F_TENTATIVE;
@@ -2685,7 +2684,7 @@ static void addrconf_add_linklocal(struct inet6_dev *idev, const struct in6_addr
2685#endif 2684#endif
2686 2685
2687 2686
2688 ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags); 2687 ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags, 0, 0);
2689 if (!IS_ERR(ifp)) { 2688 if (!IS_ERR(ifp)) {
2690 addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0); 2689 addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0);
2691 addrconf_dad_start(ifp); 2690 addrconf_dad_start(ifp);