diff options
author | Martin KaFai Lau <kafai@fb.com> | 2019-04-30 13:45:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-05-01 17:17:54 -0400 |
commit | 886b7a50100a50f1cbd08a6f8ec5884dfbe082dc (patch) | |
tree | f18e97f6f5ac9054af65539f00e52c6160470a20 /net/ipv6 | |
parent | f3505745c07ff50c22aeca9dde98762d1c8b5898 (diff) |
ipv6: A few fixes on dereferencing rt->from
It is a followup after the fix in
commit 9c69a1320515 ("route: Avoid crash from dereferencing NULL rt->from")
rt6_do_redirect():
1. NULL checking is needed on rt->from because a parallel
fib6_info delete could happen that sets rt->from to NULL.
(e.g. rt6_remove_exception() and fib6_drop_pcpu_from()).
2. fib6_info_hold() is not enough. Same reason as (1).
Meaning, holding dst->__refcnt cannot ensure
rt->from is not NULL or rt->from->fib6_ref is not 0.
Instead of using fib6_info_hold_safe() which ip6_rt_cache_alloc()
is already doing, this patch chooses to extend the rcu section
to keep "from" dereference-able after checking for NULL.
inet6_rtm_getroute():
1. NULL checking is also needed on rt->from for a similar reason.
Note that inet6_rtm_getroute() is using RTNL_FLAG_DOIT_UNLOCKED.
Fixes: a68886a69180 ("net/ipv6: Make from in rt6_info rcu protected")
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Wei Wang <weiwan@google.com>
Reviewed-by: David Ahern <dsahern@gmail.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/route.c | 38 |
1 files changed, 18 insertions, 20 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 2cc166bc978d..0520aca3354b 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -3392,11 +3392,8 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu | |||
3392 | 3392 | ||
3393 | rcu_read_lock(); | 3393 | rcu_read_lock(); |
3394 | from = rcu_dereference(rt->from); | 3394 | from = rcu_dereference(rt->from); |
3395 | /* This fib6_info_hold() is safe here because we hold reference to rt | 3395 | if (!from) |
3396 | * and rt already holds reference to fib6_info. | 3396 | goto out; |
3397 | */ | ||
3398 | fib6_info_hold(from); | ||
3399 | rcu_read_unlock(); | ||
3400 | 3397 | ||
3401 | nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL); | 3398 | nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL); |
3402 | if (!nrt) | 3399 | if (!nrt) |
@@ -3408,10 +3405,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu | |||
3408 | 3405 | ||
3409 | nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; | 3406 | nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; |
3410 | 3407 | ||
3411 | /* No need to remove rt from the exception table if rt is | 3408 | /* rt6_insert_exception() will take care of duplicated exceptions */ |
3412 | * a cached route because rt6_insert_exception() will | ||
3413 | * takes care of it | ||
3414 | */ | ||
3415 | if (rt6_insert_exception(nrt, from)) { | 3409 | if (rt6_insert_exception(nrt, from)) { |
3416 | dst_release_immediate(&nrt->dst); | 3410 | dst_release_immediate(&nrt->dst); |
3417 | goto out; | 3411 | goto out; |
@@ -3424,7 +3418,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu | |||
3424 | call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); | 3418 | call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); |
3425 | 3419 | ||
3426 | out: | 3420 | out: |
3427 | fib6_info_release(from); | 3421 | rcu_read_unlock(); |
3428 | neigh_release(neigh); | 3422 | neigh_release(neigh); |
3429 | } | 3423 | } |
3430 | 3424 | ||
@@ -5023,16 +5017,20 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, | |||
5023 | 5017 | ||
5024 | rcu_read_lock(); | 5018 | rcu_read_lock(); |
5025 | from = rcu_dereference(rt->from); | 5019 | from = rcu_dereference(rt->from); |
5026 | 5020 | if (from) { | |
5027 | if (fibmatch) | 5021 | if (fibmatch) |
5028 | err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif, | 5022 | err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, |
5029 | RTM_NEWROUTE, NETLINK_CB(in_skb).portid, | 5023 | iif, RTM_NEWROUTE, |
5030 | nlh->nlmsg_seq, 0); | 5024 | NETLINK_CB(in_skb).portid, |
5031 | else | 5025 | nlh->nlmsg_seq, 0); |
5032 | err = rt6_fill_node(net, skb, from, dst, &fl6.daddr, | 5026 | else |
5033 | &fl6.saddr, iif, RTM_NEWROUTE, | 5027 | err = rt6_fill_node(net, skb, from, dst, &fl6.daddr, |
5034 | NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, | 5028 | &fl6.saddr, iif, RTM_NEWROUTE, |
5035 | 0); | 5029 | NETLINK_CB(in_skb).portid, |
5030 | nlh->nlmsg_seq, 0); | ||
5031 | } else { | ||
5032 | err = -ENETUNREACH; | ||
5033 | } | ||
5036 | rcu_read_unlock(); | 5034 | rcu_read_unlock(); |
5037 | 5035 | ||
5038 | if (err < 0) { | 5036 | if (err < 0) { |