Skip to content

Commit

Permalink
ipv6: check fn->leaf before it is used
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Wei Wang authored and David S. Miller committed Oct 7, 2017
1 parent bbd63f0 commit 8d1040e
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 13 deletions.
23 changes: 18 additions & 5 deletions net/ipv6/ip6_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;

Expand Down Expand Up @@ -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
Expand All @@ -1372,6 +1384,7 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,

prev = fn;

next:
/*
* We have more bits to go
*/
Expand Down
20 changes: 12 additions & 8 deletions net/ipv6/route.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -748,31 +749,34 @@ 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) {
struct rt6_info *next = rt0->dst.rt6_next;

/* 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;
}

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 8d1040e

Please sign in to comment.