ipv6: check fn->leaf before it is used
If rwlock is replaced with rcu and spinlock, it is possible that the reader thread will see fn->leaf as NULL in the following scenarios: 1. fib6_add() is in progress and we have already inserted a new node but not yet inserted the route. 2. fib6_del_route() is in progress and we have already set fn->leaf to NULL but not yet freed the node because of rcu grace period. This patch makes sure all the reader threads check fn->leaf first before using it. And together with later patch to grab rcu_read_lock() and rcu_dereference() fn->leaf, it makes sure reader threads are safe when accessing fn->leaf. Signed-off-by: Wei Wang <weiwan@google.com> Signed-off-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bbd63f06d1
commit
8d1040e808
|
@ -1279,10 +1279,13 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
|
|||
|
||||
while (fn) {
|
||||
if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
|
||||
struct rt6_info *leaf = fn->leaf;
|
||||
struct rt6key *key;
|
||||
|
||||
key = (struct rt6key *) ((u8 *) fn->leaf +
|
||||
args->offset);
|
||||
if (!leaf)
|
||||
goto backtrack;
|
||||
|
||||
key = (struct rt6key *) ((u8 *)leaf + args->offset);
|
||||
|
||||
if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
|
@ -1299,9 +1302,7 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
|
|||
return fn;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_IPV6_SUBTREES
|
||||
backtrack:
|
||||
#endif
|
||||
if (fn->fn_flags & RTN_ROOT)
|
||||
break;
|
||||
|
||||
|
@ -1358,7 +1359,18 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,
|
|||
struct fib6_node *fn, *prev = NULL;
|
||||
|
||||
for (fn = root; fn ; ) {
|
||||
struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);
|
||||
struct rt6_info *leaf = fn->leaf;
|
||||
struct rt6key *key;
|
||||
|
||||
/* This node is being deleted */
|
||||
if (!leaf) {
|
||||
if (plen <= fn->fn_bit)
|
||||
goto out;
|
||||
else
|
||||
goto next;
|
||||
}
|
||||
|
||||
key = (struct rt6key *)((u8 *)leaf + offset);
|
||||
|
||||
/*
|
||||
* Prefix match
|
||||
|
@ -1372,6 +1384,7 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,
|
|||
|
||||
prev = fn;
|
||||
|
||||
next:
|
||||
/*
|
||||
* We have more bits to go
|
||||
*/
|
||||
|
|
|
@ -712,6 +712,7 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
|
|||
}
|
||||
|
||||
static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
|
||||
struct rt6_info *leaf,
|
||||
struct rt6_info *rr_head,
|
||||
u32 metric, int oif, int strict,
|
||||
bool *do_rr)
|
||||
|
@ -730,7 +731,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
|
|||
match = find_match(rt, oif, strict, &mpri, match, do_rr);
|
||||
}
|
||||
|
||||
for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
|
||||
for (rt = leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
|
||||
if (rt->rt6i_metric != metric) {
|
||||
cont = rt;
|
||||
break;
|
||||
|
@ -748,17 +749,21 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
|
|||
return match;
|
||||
}
|
||||
|
||||
static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
|
||||
static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn,
|
||||
int oif, int strict)
|
||||
{
|
||||
struct rt6_info *leaf = fn->leaf;
|
||||
struct rt6_info *match, *rt0;
|
||||
struct net *net;
|
||||
bool do_rr = false;
|
||||
|
||||
if (!leaf)
|
||||
return net->ipv6.ip6_null_entry;
|
||||
|
||||
rt0 = fn->rr_ptr;
|
||||
if (!rt0)
|
||||
fn->rr_ptr = rt0 = fn->leaf;
|
||||
fn->rr_ptr = rt0 = leaf;
|
||||
|
||||
match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
|
||||
match = find_rr_leaf(fn, leaf, rt0, rt0->rt6i_metric, oif, strict,
|
||||
&do_rr);
|
||||
|
||||
if (do_rr) {
|
||||
|
@ -766,13 +771,12 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
|
|||
|
||||
/* no entries matched; do round-robin */
|
||||
if (!next || next->rt6i_metric != rt0->rt6i_metric)
|
||||
next = fn->leaf;
|
||||
next = leaf;
|
||||
|
||||
if (next != rt0)
|
||||
fn->rr_ptr = next;
|
||||
}
|
||||
|
||||
net = dev_net(rt0->dst.dev);
|
||||
return match ? match : net->ipv6.ip6_null_entry;
|
||||
}
|
||||
|
||||
|
@ -1623,7 +1627,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
|
|||
oif = 0;
|
||||
|
||||
redo_rt6_select:
|
||||
rt = rt6_select(fn, oif, strict);
|
||||
rt = rt6_select(net, fn, oif, strict);
|
||||
if (rt->rt6i_nsiblings)
|
||||
rt = rt6_multipath_select(rt, fl6, oif, strict);
|
||||
if (rt == net->ipv6.ip6_null_entry) {
|
||||
|
|
Loading…
Reference in New Issue