aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorYOSHIFUJI Hideaki / 吉藤英明 <yoshfuji@linux-ipv6.org>2013-02-19 19:29:08 -0500
committerDavid S. Miller <davem@davemloft.net>2013-02-20 15:11:45 -0500
commitecd9883724b78cc72ed92c98bcb1a46c764fff21 (patch)
treec18d4c96f7e4dcf278c88e505be8225af7374800 /net/ipv6
parent68534c682e8f5c333f835818ca5a89d3e6288870 (diff)
ipv6: fix race condition regarding dst->expires and dst->from.
Eric Dumazet wrote: | Some strange crashes happen in rt6_check_expired(), with access | to random addresses. | | At first glance, it looks like the RTF_EXPIRES and | stuff added in commit 1716a96101c49186b | (ipv6: fix problem with expired dst cache) | are racy : same dst could be manipulated at the same time | on different cpus. | | At some point, our stack believes rt->dst.from contains a dst pointer, | while its really a jiffie value (as rt->dst.expires shares the same area | of memory) | | rt6_update_expires() should be fixed, or am I missing something ? | | CC Neil because of https://bugzilla.redhat.com/show_bug.cgi?id=892060 Because we do not have any locks for dst_entry, we cannot change essential structure in the entry; e.g., we cannot change reference to other entity. To fix this issue, split 'from' and 'expires' field in dst_entry out of union. Once it is 'from' is assigned in the constructor, keep the reference until the very last stage of the life time of the object. Of course, it is unsafe to change 'from', so make rt6_set_from simple just for fresh entries. Reported-by: Eric Dumazet <eric.dumazet@gmail.com> Reported-by: Neil Horman <nhorman@tuxdriver.com> CC: Gao Feng <gaofeng@cn.fujitsu.com> Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> Reviewed-by: Eric Dumazet <edumazet@google.com> Reported-by: Steinar H. Gunderson <sesse@google.com> Reviewed-by: Neil Horman <nhorman@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/route.c8
1 files changed, 3 insertions, 5 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 515bb51e05a8..928266569689 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -287,6 +287,7 @@ static void ip6_dst_destroy(struct dst_entry *dst)
287{ 287{
288 struct rt6_info *rt = (struct rt6_info *)dst; 288 struct rt6_info *rt = (struct rt6_info *)dst;
289 struct inet6_dev *idev = rt->rt6i_idev; 289 struct inet6_dev *idev = rt->rt6i_idev;
290 struct dst_entry *from = dst->from;
290 291
291 if (!(rt->dst.flags & DST_HOST)) 292 if (!(rt->dst.flags & DST_HOST))
292 dst_destroy_metrics_generic(dst); 293 dst_destroy_metrics_generic(dst);
@@ -296,8 +297,8 @@ static void ip6_dst_destroy(struct dst_entry *dst)
296 in6_dev_put(idev); 297 in6_dev_put(idev);
297 } 298 }
298 299
299 if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) 300 dst->from = NULL;
300 dst_release(dst->from); 301 dst_release(from);
301 302
302 if (rt6_has_peer(rt)) { 303 if (rt6_has_peer(rt)) {
303 struct inet_peer *peer = rt6_peer_ptr(rt); 304 struct inet_peer *peer = rt6_peer_ptr(rt);
@@ -1010,7 +1011,6 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori
1010 1011
1011 rt->rt6i_gateway = ort->rt6i_gateway; 1012 rt->rt6i_gateway = ort->rt6i_gateway;
1012 rt->rt6i_flags = ort->rt6i_flags; 1013 rt->rt6i_flags = ort->rt6i_flags;
1013 rt6_clean_expires(rt);
1014 rt->rt6i_metric = 0; 1014 rt->rt6i_metric = 0;
1015 1015
1016 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); 1016 memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
@@ -1784,8 +1784,6 @@ static struct rt6_info *ip6_rt_copy(struct rt6_info *ort,
1784 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) == 1784 if ((ort->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) ==
1785 (RTF_DEFAULT | RTF_ADDRCONF)) 1785 (RTF_DEFAULT | RTF_ADDRCONF))
1786 rt6_set_from(rt, ort); 1786 rt6_set_from(rt, ort);
1787 else
1788 rt6_clean_expires(rt);
1789 rt->rt6i_metric = 0; 1787 rt->rt6i_metric = 0;
1790 1788
1791#ifdef CONFIG_IPV6_SUBTREES 1789#ifdef CONFIG_IPV6_SUBTREES