Commit 6b18bdfd authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ipv6-fib6_ref-conversion-to-refcount_t'



Eric Dumazet says:

====================
ipv6: fib6_ref conversion to refcount_t

We are chasing use-after-free in IPv6 that could have their origin
in fib6_ref 0 -> 1 transitions.

This patch series should help finding the root causes if these
illegal transitions ever happen.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 20eb08b2 f05713e0
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -146,7 +146,7 @@ struct fib6_info {
	struct list_head		fib6_siblings;
	unsigned int			fib6_nsiblings;

	atomic_t			fib6_ref;
	refcount_t			fib6_ref;
	unsigned long			expires;
	struct dst_metrics		*fib6_metrics;
#define fib6_pmtu		fib6_metrics->metrics[RTAX_MTU-1]
@@ -284,17 +284,17 @@ void fib6_info_destroy_rcu(struct rcu_head *head);

static inline void fib6_info_hold(struct fib6_info *f6i)
{
	atomic_inc(&f6i->fib6_ref);
	refcount_inc(&f6i->fib6_ref);
}

static inline bool fib6_info_hold_safe(struct fib6_info *f6i)
{
	return atomic_inc_not_zero(&f6i->fib6_ref);
	return refcount_inc_not_zero(&f6i->fib6_ref);
}

static inline void fib6_info_release(struct fib6_info *f6i)
{
	if (f6i && atomic_dec_and_test(&f6i->fib6_ref))
	if (f6i && refcount_dec_and_test(&f6i->fib6_ref))
		call_rcu(&f6i->rcu, fib6_info_destroy_rcu);
}

+11 −14
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags)
	}

	INIT_LIST_HEAD(&f6i->fib6_siblings);
	atomic_inc(&f6i->fib6_ref);
	refcount_set(&f6i->fib6_ref, 1);

	return f6i;
}
@@ -175,10 +175,7 @@ void fib6_info_destroy_rcu(struct rcu_head *head)
	WARN_ON(f6i->fib6_node);

	bucket = rcu_dereference_protected(f6i->rt6i_exception_bucket, 1);
	if (bucket) {
		f6i->rt6i_exception_bucket = NULL;
	kfree(bucket);
	}

	if (f6i->rt6i_pcpu) {
		int cpu;
@@ -849,8 +846,8 @@ insert_above:

		RCU_INIT_POINTER(in->parent, pn);
		in->leaf = fn->leaf;
		atomic_inc(&rcu_dereference_protected(in->leaf,
				lockdep_is_held(&table->tb6_lock))->fib6_ref);
		fib6_info_hold(rcu_dereference_protected(in->leaf,
				lockdep_is_held(&table->tb6_lock)));

		/* update parent pointer */
		if (dir)
@@ -932,7 +929,7 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
{
	struct fib6_table *table = rt->fib6_table;

	if (atomic_read(&rt->fib6_ref) != 1) {
	if (refcount_read(&rt->fib6_ref) != 1) {
		/* This route is used as dummy address holder in some split
		 * nodes. It is not leaked, but it still holds other resources,
		 * which must be released in time. So, scan ascendant nodes
@@ -945,7 +942,7 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
			struct fib6_info *new_leaf;
			if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) {
				new_leaf = fib6_find_prefix(net, table, fn);
				atomic_inc(&new_leaf->fib6_ref);
				fib6_info_hold(new_leaf);

				rcu_assign_pointer(fn->leaf, new_leaf);
				fib6_info_release(rt);
@@ -1111,7 +1108,7 @@ add:
			return err;

		rcu_assign_pointer(rt->fib6_next, iter);
		atomic_inc(&rt->fib6_ref);
		fib6_info_hold(rt);
		rcu_assign_pointer(rt->fib6_node, fn);
		rcu_assign_pointer(*ins, rt);
		if (!info->skip_notify)
@@ -1139,7 +1136,7 @@ add:
		if (err)
			return err;

		atomic_inc(&rt->fib6_ref);
		fib6_info_hold(rt);
		rcu_assign_pointer(rt->fib6_node, fn);
		rt->fib6_next = iter->fib6_next;
		rcu_assign_pointer(*ins, rt);
@@ -1281,7 +1278,7 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
			if (!sfn)
				goto failure;

			atomic_inc(&info->nl_net->ipv6.fib6_null_entry->fib6_ref);
			fib6_info_hold(info->nl_net->ipv6.fib6_null_entry);
			rcu_assign_pointer(sfn->leaf,
					   info->nl_net->ipv6.fib6_null_entry);
			sfn->fn_flags = RTN_ROOT;
@@ -1324,7 +1321,7 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
				rcu_assign_pointer(fn->leaf,
					    info->nl_net->ipv6.fib6_null_entry);
			} else {
				atomic_inc(&rt->fib6_ref);
				fib6_info_hold(rt);
				rcu_assign_pointer(fn->leaf, rt);
			}
		}
@@ -2314,7 +2311,7 @@ static int ipv6_route_seq_show(struct seq_file *seq, void *v)

	dev = rt->fib6_nh.fib_nh_dev;
	seq_printf(seq, " %08x %08x %08x %08x %8s\n",
		   rt->fib6_metric, atomic_read(&rt->fib6_ref), 0,
		   rt->fib6_metric, refcount_read(&rt->fib6_ref), 0,
		   flags, dev ? dev->name : "");
	iter->w.leaf = NULL;
	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -296,7 +296,7 @@ static const struct fib6_info fib6_null_entry_template = {
	.fib6_flags	= (RTF_REJECT | RTF_NONEXTHOP),
	.fib6_protocol  = RTPROT_KERNEL,
	.fib6_metric	= ~(u32)0,
	.fib6_ref	= ATOMIC_INIT(1),
	.fib6_ref	= REFCOUNT_INIT(1),
	.fib6_type	= RTN_UNREACHABLE,
	.fib6_metrics	= (struct dst_metrics *)&dst_default_metrics,
};