diff options
author | Wei Wang <weiwan@google.com> | 2017-10-06 12:06:08 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-10-07 21:22:58 +0100 |
commit | 8d1040e808bb2a5aeb4f0791b32bc7356a01ab11 (patch) | |
tree | f4bf4d0471b7d5c75c21f5990fecac90fdb1f57d /net/ipv6/ip6_fib.c | |
parent | bbd63f06d114a52be33f6982fc89ca2768cdeb62 (diff) | |
download | linux-8d1040e808bb2a5aeb4f0791b32bc7356a01ab11.tar.bz2 |
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>
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 23 |
1 files changed, 18 insertions, 5 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index f604b311cc3e..cf6137e81408 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -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 */ |