Commit 8d1040e8 authored by Wei Wang's avatar Wei Wang Committed by David S. Miller

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: default avatarWei Wang <weiwan@google.com>
Signed-off-by: default avatarMartin KaFai Lau <kafai@fb.com>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bbd63f06
...@@ -1279,10 +1279,13 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root, ...@@ -1279,10 +1279,13 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
while (fn) { while (fn) {
if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) { if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
struct rt6_info *leaf = fn->leaf;
struct rt6key *key; struct rt6key *key;
key = (struct rt6key *) ((u8 *) fn->leaf + if (!leaf)
args->offset); goto backtrack;
key = (struct rt6key *) ((u8 *)leaf + args->offset);
if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) { if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
#ifdef CONFIG_IPV6_SUBTREES #ifdef CONFIG_IPV6_SUBTREES
...@@ -1299,9 +1302,7 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root, ...@@ -1299,9 +1302,7 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
return fn; return fn;
} }
} }
#ifdef CONFIG_IPV6_SUBTREES
backtrack: backtrack:
#endif
if (fn->fn_flags & RTN_ROOT) if (fn->fn_flags & RTN_ROOT)
break; break;
...@@ -1358,7 +1359,18 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root, ...@@ -1358,7 +1359,18 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,
struct fib6_node *fn, *prev = NULL; struct fib6_node *fn, *prev = NULL;
for (fn = root; fn ; ) { 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 * Prefix match
...@@ -1372,6 +1384,7 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root, ...@@ -1372,6 +1384,7 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,
prev = fn; prev = fn;
next:
/* /*
* We have more bits to go * We have more bits to go
*/ */
......
...@@ -712,6 +712,7 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, ...@@ -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, static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
struct rt6_info *leaf,
struct rt6_info *rr_head, struct rt6_info *rr_head,
u32 metric, int oif, int strict, u32 metric, int oif, int strict,
bool *do_rr) bool *do_rr)
...@@ -730,7 +731,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn, ...@@ -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); 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) { if (rt->rt6i_metric != metric) {
cont = rt; cont = rt;
break; break;
...@@ -748,17 +749,21 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn, ...@@ -748,17 +749,21 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
return match; 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 rt6_info *match, *rt0;
struct net *net;
bool do_rr = false; bool do_rr = false;
if (!leaf)
return net->ipv6.ip6_null_entry;
rt0 = fn->rr_ptr; rt0 = fn->rr_ptr;
if (!rt0) 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); &do_rr);
if (do_rr) { if (do_rr) {
...@@ -766,13 +771,12 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict) ...@@ -766,13 +771,12 @@ static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
/* no entries matched; do round-robin */ /* no entries matched; do round-robin */
if (!next || next->rt6i_metric != rt0->rt6i_metric) if (!next || next->rt6i_metric != rt0->rt6i_metric)
next = fn->leaf; next = leaf;
if (next != rt0) if (next != rt0)
fn->rr_ptr = next; fn->rr_ptr = next;
} }
net = dev_net(rt0->dst.dev);
return match ? match : net->ipv6.ip6_null_entry; 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, ...@@ -1623,7 +1627,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
oif = 0; oif = 0;
redo_rt6_select: redo_rt6_select:
rt = rt6_select(fn, oif, strict); rt = rt6_select(net, fn, oif, strict);
if (rt->rt6i_nsiblings) if (rt->rt6i_nsiblings)
rt = rt6_multipath_select(rt, fl6, oif, strict); rt = rt6_multipath_select(rt, fl6, oif, strict);
if (rt == net->ipv6.ip6_null_entry) { if (rt == net->ipv6.ip6_null_entry) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment