Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+18 −5
Original line number Original line Diff line number Diff line
@@ -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,
					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,
	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,


		prev = fn;
		prev = fn;


next:
		/*
		/*
		 *	We have more bits to go
		 *	We have more bits to go
		 */
		 */
+12 −8
Original line number Original line Diff line number Diff line
@@ -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,
		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,
	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)


		/* 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,
		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) {