Skip to content

Commit 8d1040e

Browse files
tracywwnjdavem330
authored andcommitted
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 <[email protected]> Signed-off-by: Martin KaFai Lau <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent bbd63f0 commit 8d1040e

File tree

2 files changed

+30
-13
lines changed

2 files changed

+30
-13
lines changed

net/ipv6/ip6_fib.c

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,10 +1279,13 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
12791279

12801280
while (fn) {
12811281
if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) {
1282+
struct rt6_info *leaf = fn->leaf;
12821283
struct rt6key *key;
12831284

1284-
key = (struct rt6key *) ((u8 *) fn->leaf +
1285-
args->offset);
1285+
if (!leaf)
1286+
goto backtrack;
1287+
1288+
key = (struct rt6key *) ((u8 *)leaf + args->offset);
12861289

12871290
if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) {
12881291
#ifdef CONFIG_IPV6_SUBTREES
@@ -1299,9 +1302,7 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root,
12991302
return fn;
13001303
}
13011304
}
1302-
#ifdef CONFIG_IPV6_SUBTREES
13031305
backtrack:
1304-
#endif
13051306
if (fn->fn_flags & RTN_ROOT)
13061307
break;
13071308

@@ -1358,7 +1359,18 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,
13581359
struct fib6_node *fn, *prev = NULL;
13591360

13601361
for (fn = root; fn ; ) {
1361-
struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset);
1362+
struct rt6_info *leaf = fn->leaf;
1363+
struct rt6key *key;
1364+
1365+
/* This node is being deleted */
1366+
if (!leaf) {
1367+
if (plen <= fn->fn_bit)
1368+
goto out;
1369+
else
1370+
goto next;
1371+
}
1372+
1373+
key = (struct rt6key *)((u8 *)leaf + offset);
13621374

13631375
/*
13641376
* Prefix match
@@ -1372,6 +1384,7 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root,
13721384

13731385
prev = fn;
13741386

1387+
next:
13751388
/*
13761389
* We have more bits to go
13771390
*/

net/ipv6/route.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
712712
}
713713

714714
static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
715+
struct rt6_info *leaf,
715716
struct rt6_info *rr_head,
716717
u32 metric, int oif, int strict,
717718
bool *do_rr)
@@ -730,7 +731,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
730731
match = find_match(rt, oif, strict, &mpri, match, do_rr);
731732
}
732733

733-
for (rt = fn->leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
734+
for (rt = leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) {
734735
if (rt->rt6i_metric != metric) {
735736
cont = rt;
736737
break;
@@ -748,31 +749,34 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn,
748749
return match;
749750
}
750751

751-
static struct rt6_info *rt6_select(struct fib6_node *fn, int oif, int strict)
752+
static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn,
753+
int oif, int strict)
752754
{
755+
struct rt6_info *leaf = fn->leaf;
753756
struct rt6_info *match, *rt0;
754-
struct net *net;
755757
bool do_rr = false;
756758

759+
if (!leaf)
760+
return net->ipv6.ip6_null_entry;
761+
757762
rt0 = fn->rr_ptr;
758763
if (!rt0)
759-
fn->rr_ptr = rt0 = fn->leaf;
764+
fn->rr_ptr = rt0 = leaf;
760765

761-
match = find_rr_leaf(fn, rt0, rt0->rt6i_metric, oif, strict,
766+
match = find_rr_leaf(fn, leaf, rt0, rt0->rt6i_metric, oif, strict,
762767
&do_rr);
763768

764769
if (do_rr) {
765770
struct rt6_info *next = rt0->dst.rt6_next;
766771

767772
/* no entries matched; do round-robin */
768773
if (!next || next->rt6i_metric != rt0->rt6i_metric)
769-
next = fn->leaf;
774+
next = leaf;
770775

771776
if (next != rt0)
772777
fn->rr_ptr = next;
773778
}
774779

775-
net = dev_net(rt0->dst.dev);
776780
return match ? match : net->ipv6.ip6_null_entry;
777781
}
778782

@@ -1623,7 +1627,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
16231627
oif = 0;
16241628

16251629
redo_rt6_select:
1626-
rt = rt6_select(fn, oif, strict);
1630+
rt = rt6_select(net, fn, oif, strict);
16271631
if (rt->rt6i_nsiblings)
16281632
rt = rt6_multipath_select(rt, fl6, oif, strict);
16291633
if (rt == net->ipv6.ip6_null_entry) {

0 commit comments

Comments
 (0)