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

Merge branch 'net_sched-fix-a-UAF-in-tcf_action_init'



Cong Wang says:

====================
net_sched: fix a UAF in tcf_action_init()

This patchset fixes a use-after-free triggered by syzbot. Please
find more details in each patch description.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 7241c5a6 0fedc63f
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -166,8 +166,6 @@ int tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index,
			      struct nlattr *est, struct tc_action **a,
			      const struct tc_action_ops *ops, int bind,
			      u32 flags);
void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a);

void tcf_idr_cleanup(struct tc_action_net *tn, u32 index);
int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
			struct tc_action **a, int bind);
+34 −18
Original line number Diff line number Diff line
@@ -307,6 +307,8 @@ static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,

	mutex_lock(&idrinfo->lock);
	idr_for_each_entry_ul(idr, p, tmp, id) {
		if (IS_ERR(p))
			continue;
		ret = tcf_idr_release_unsafe(p);
		if (ret == ACT_P_DELETED) {
			module_put(ops->owner);
@@ -467,17 +469,6 @@ int tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index,
}
EXPORT_SYMBOL(tcf_idr_create_from_flags);

void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a)
{
	struct tcf_idrinfo *idrinfo = tn->idrinfo;

	mutex_lock(&idrinfo->lock);
	/* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
	WARN_ON(!IS_ERR(idr_replace(&idrinfo->action_idr, a, a->tcfa_index)));
	mutex_unlock(&idrinfo->lock);
}
EXPORT_SYMBOL(tcf_idr_insert);

/* Cleanup idr index that was allocated but not initialized. */

void tcf_idr_cleanup(struct tc_action_net *tn, u32 index)
@@ -902,6 +893,26 @@ static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = {
	[TCA_ACT_HW_STATS]	= NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY),
};

static void tcf_idr_insert_many(struct tc_action *actions[])
{
	int i;

	for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
		struct tc_action *a = actions[i];
		struct tcf_idrinfo *idrinfo;

		if (!a)
			continue;
		idrinfo = a->idrinfo;
		mutex_lock(&idrinfo->lock);
		/* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if
		 * it is just created, otherwise this is just a nop.
		 */
		idr_replace(&idrinfo->action_idr, a, a->tcfa_index);
		mutex_unlock(&idrinfo->lock);
	}
}

struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
				    struct nlattr *nla, struct nlattr *est,
				    char *name, int ovr, int bind,
@@ -989,6 +1000,13 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
	if (err < 0)
		goto err_mod;

	if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN) &&
	    !rcu_access_pointer(a->goto_chain)) {
		tcf_action_destroy_1(a, bind);
		NL_SET_ERR_MSG(extack, "can't use goto chain with NULL chain");
		return ERR_PTR(-EINVAL);
	}

	if (!name && tb[TCA_ACT_COOKIE])
		tcf_set_action_cookie(&a->act_cookie, cookie);

@@ -1002,13 +1020,6 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
	if (err != ACT_P_CREATED)
		module_put(a_o->owner);

	if (TC_ACT_EXT_CMP(a->tcfa_action, TC_ACT_GOTO_CHAIN) &&
	    !rcu_access_pointer(a->goto_chain)) {
		tcf_action_destroy_1(a, bind);
		NL_SET_ERR_MSG(extack, "can't use goto chain with NULL chain");
		return ERR_PTR(-EINVAL);
	}

	return a;

err_mod:
@@ -1051,6 +1062,11 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
		actions[i - 1] = act;
	}

	/* We have to commit them all together, because if any error happened in
	 * between, we could not handle the failure gracefully.
	 */
	tcf_idr_insert_many(actions);

	*attr_size = tcf_action_full_attrs_size(sz);
	return i - 1;

+1 −3
Original line number Diff line number Diff line
@@ -365,9 +365,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
	if (goto_ch)
		tcf_chain_put_by_act(goto_ch);

	if (res == ACT_P_CREATED) {
		tcf_idr_insert(tn, *act);
	} else {
	if (res != ACT_P_CREATED) {
		/* make sure the program being replaced is no longer executing */
		synchronize_rcu();
		tcf_bpf_cfg_cleanup(&old);
+0 −1
Original line number Diff line number Diff line
@@ -139,7 +139,6 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
		ci->net = net;
		ci->zone = parm->zone;

		tcf_idr_insert(tn, *a);
		ret = ACT_P_CREATED;
	} else if (ret > 0) {
		ci = to_connmark(*a);
+0 −3
Original line number Diff line number Diff line
@@ -110,9 +110,6 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla,
	if (params_new)
		kfree_rcu(params_new, rcu);

	if (ret == ACT_P_CREATED)
		tcf_idr_insert(tn, *a);

	return ret;
put_chain:
	if (goto_ch)
Loading