ipv6: A few fixes on dereferencing rt->from
It is a followup after the fix in commit9c69a13205
("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:a68886a691
("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>
This commit is contained in:
parent
f3505745c0
commit
886b7a5010
|
@ -3392,11 +3392,8 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
from = rcu_dereference(rt->from);
|
from = rcu_dereference(rt->from);
|
||||||
/* This fib6_info_hold() is safe here because we hold reference to rt
|
if (!from)
|
||||||
* and rt already holds reference to fib6_info.
|
goto out;
|
||||||
*/
|
|
||||||
fib6_info_hold(from);
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL);
|
nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL);
|
||||||
if (!nrt)
|
if (!nrt)
|
||||||
|
@ -3408,10 +3405,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||||
|
|
||||||
nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
|
nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key;
|
||||||
|
|
||||||
/* No need to remove rt from the exception table if rt is
|
/* rt6_insert_exception() will take care of duplicated exceptions */
|
||||||
* a cached route because rt6_insert_exception() will
|
|
||||||
* takes care of it
|
|
||||||
*/
|
|
||||||
if (rt6_insert_exception(nrt, from)) {
|
if (rt6_insert_exception(nrt, from)) {
|
||||||
dst_release_immediate(&nrt->dst);
|
dst_release_immediate(&nrt->dst);
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -3424,7 +3418,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
||||||
call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
|
call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
fib6_info_release(from);
|
rcu_read_unlock();
|
||||||
neigh_release(neigh);
|
neigh_release(neigh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5023,16 +5017,20 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
from = rcu_dereference(rt->from);
|
from = rcu_dereference(rt->from);
|
||||||
|
if (from) {
|
||||||
if (fibmatch)
|
if (fibmatch)
|
||||||
err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
|
err = rt6_fill_node(net, skb, from, NULL, NULL, NULL,
|
||||||
RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
|
iif, RTM_NEWROUTE,
|
||||||
nlh->nlmsg_seq, 0);
|
NETLINK_CB(in_skb).portid,
|
||||||
else
|
nlh->nlmsg_seq, 0);
|
||||||
err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
|
else
|
||||||
&fl6.saddr, iif, RTM_NEWROUTE,
|
err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
|
||||||
NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
|
&fl6.saddr, iif, RTM_NEWROUTE,
|
||||||
0);
|
NETLINK_CB(in_skb).portid,
|
||||||
|
nlh->nlmsg_seq, 0);
|
||||||
|
} else {
|
||||||
|
err = -ENETUNREACH;
|
||||||
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
Loading…
Reference in New Issue