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

Commit b9a24bb7 authored by WANG Cong's avatar WANG Cong Committed by David S. Miller
Browse files

net_sched: properly handle failure case of tcf_exts_init()



After commit 22dc13c8 ("net_sched: convert tcf_exts from list to pointer array")
we do dynamic allocation in tcf_exts_init(), therefore we need
to handle the ENOMEM case properly.

Cc: Jamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarCong Wang <xiyou.wangcong@gmail.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Acked-by: default avatarJamal Hadi Salim <jhs@mojatatu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c1346a7e
Loading
Loading
Loading
Loading
+4 −2
Original line number Original line Diff line number Diff line
@@ -69,17 +69,19 @@ struct tcf_exts {
	int police;
	int police;
};
};


static inline void tcf_exts_init(struct tcf_exts *exts, int action, int police)
static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police)
{
{
#ifdef CONFIG_NET_CLS_ACT
#ifdef CONFIG_NET_CLS_ACT
	exts->type = 0;
	exts->type = 0;
	exts->nr_actions = 0;
	exts->nr_actions = 0;
	exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
	exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
				GFP_KERNEL);
				GFP_KERNEL);
	WARN_ON(!exts->actions); /* TODO: propagate the error to callers */
	if (!exts->actions)
		return -ENOMEM;
#endif
#endif
	exts->action = action;
	exts->action = action;
	exts->police = police;
	exts->police = police;
	return 0;
}
}


/**
/**
+9 −3
Original line number Original line Diff line number Diff line
@@ -138,10 +138,12 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
	struct tcf_exts e;
	struct tcf_exts e;
	struct tcf_ematch_tree t;
	struct tcf_ematch_tree t;


	tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	err = tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
	if (err < 0)
	if (err < 0)
		return err;
		return err;
	err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
	if (err < 0)
		goto errout;


	err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &t);
	err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &t);
	if (err < 0)
	if (err < 0)
@@ -189,7 +191,10 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
	if (!fnew)
	if (!fnew)
		return -ENOBUFS;
		return -ENOBUFS;


	tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	err = tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	if (err < 0)
		goto errout;

	err = -EINVAL;
	err = -EINVAL;
	if (handle) {
	if (handle) {
		fnew->handle = handle;
		fnew->handle = handle;
@@ -226,6 +231,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,


	return 0;
	return 0;
errout:
errout:
	tcf_exts_destroy(&fnew->exts);
	kfree(fnew);
	kfree(fnew);
	return err;
	return err;
}
}
+17 −10
Original line number Original line Diff line number Diff line
@@ -311,17 +311,19 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
	if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf))
	if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf))
		return -EINVAL;
		return -EINVAL;


	tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
	ret = tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
	ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr);
	if (ret < 0)
	if (ret < 0)
		return ret;
		return ret;
	ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr);
	if (ret < 0)
		goto errout;


	if (tb[TCA_BPF_FLAGS]) {
	if (tb[TCA_BPF_FLAGS]) {
		u32 bpf_flags = nla_get_u32(tb[TCA_BPF_FLAGS]);
		u32 bpf_flags = nla_get_u32(tb[TCA_BPF_FLAGS]);


		if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) {
		if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) {
			tcf_exts_destroy(&exts);
			ret = -EINVAL;
			return -EINVAL;
			goto errout;
		}
		}


		have_exts = bpf_flags & TCA_BPF_FLAG_ACT_DIRECT;
		have_exts = bpf_flags & TCA_BPF_FLAG_ACT_DIRECT;
@@ -331,10 +333,8 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,


	ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) :
	ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) :
		       cls_bpf_prog_from_efd(tb, prog, tp);
		       cls_bpf_prog_from_efd(tb, prog, tp);
	if (ret < 0) {
	if (ret < 0)
		tcf_exts_destroy(&exts);
		goto errout;
		return ret;
	}


	if (tb[TCA_BPF_CLASSID]) {
	if (tb[TCA_BPF_CLASSID]) {
		prog->res.classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
		prog->res.classid = nla_get_u32(tb[TCA_BPF_CLASSID]);
@@ -343,6 +343,10 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,


	tcf_exts_change(tp, &prog->exts, &exts);
	tcf_exts_change(tp, &prog->exts, &exts);
	return 0;
	return 0;

errout:
	tcf_exts_destroy(&exts);
	return ret;
}
}


static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp,
@@ -388,7 +392,9 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
	if (!prog)
	if (!prog)
		return -ENOBUFS;
		return -ENOBUFS;


	tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE);
	ret = tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE);
	if (ret < 0)
		goto errout;


	if (oldprog) {
	if (oldprog) {
		if (handle && oldprog->handle != handle) {
		if (handle && oldprog->handle != handle) {
@@ -420,9 +426,10 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,


	*arg = (unsigned long) prog;
	*arg = (unsigned long) prog;
	return 0;
	return 0;

errout:
errout:
	tcf_exts_destroy(&prog->exts);
	kfree(prog);
	kfree(prog);

	return ret;
	return ret;
}
}


+10 −3
Original line number Original line Diff line number Diff line
@@ -93,7 +93,9 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
	if (!new)
	if (!new)
		return -ENOBUFS;
		return -ENOBUFS;


	tcf_exts_init(&new->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
	err = tcf_exts_init(&new->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
	if (err < 0)
		goto errout;
	new->handle = handle;
	new->handle = handle;
	new->tp = tp;
	new->tp = tp;
	err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS],
	err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS],
@@ -101,10 +103,14 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
	if (err < 0)
	if (err < 0)
		goto errout;
		goto errout;


	tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
	err = tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
	err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
	if (err < 0)
	if (err < 0)
		goto errout;
		goto errout;
	err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
	if (err < 0) {
		tcf_exts_destroy(&e);
		goto errout;
	}


	err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t);
	err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t);
	if (err < 0) {
	if (err < 0) {
@@ -120,6 +126,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
		call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
		call_rcu(&head->rcu, cls_cgroup_destroy_rcu);
	return 0;
	return 0;
errout:
errout:
	tcf_exts_destroy(&new->exts);
	kfree(new);
	kfree(new);
	return err;
	return err;
}
}
+16 −10
Original line number Original line Diff line number Diff line
@@ -418,10 +418,12 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
			return -EOPNOTSUPP;
			return -EOPNOTSUPP;
	}
	}


	tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE);
	err = tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE);
	if (err < 0)
		goto err1;
	err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
	err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
	if (err < 0)
	if (err < 0)
		return err;
		goto err1;


	err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &t);
	err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &t);
	if (err < 0)
	if (err < 0)
@@ -432,13 +434,15 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
	if (!fnew)
	if (!fnew)
		goto err2;
		goto err2;


	tcf_exts_init(&fnew->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE);
	err = tcf_exts_init(&fnew->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE);
	if (err < 0)
		goto err3;


	fold = (struct flow_filter *)*arg;
	fold = (struct flow_filter *)*arg;
	if (fold) {
	if (fold) {
		err = -EINVAL;
		err = -EINVAL;
		if (fold->handle != handle && handle)
		if (fold->handle != handle && handle)
			goto err2;
			goto err3;


		/* Copy fold into fnew */
		/* Copy fold into fnew */
		fnew->tp = fold->tp;
		fnew->tp = fold->tp;
@@ -458,31 +462,31 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
		if (tb[TCA_FLOW_MODE])
		if (tb[TCA_FLOW_MODE])
			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
		if (mode != FLOW_MODE_HASH && nkeys > 1)
		if (mode != FLOW_MODE_HASH && nkeys > 1)
			goto err2;
			goto err3;


		if (mode == FLOW_MODE_HASH)
		if (mode == FLOW_MODE_HASH)
			perturb_period = fold->perturb_period;
			perturb_period = fold->perturb_period;
		if (tb[TCA_FLOW_PERTURB]) {
		if (tb[TCA_FLOW_PERTURB]) {
			if (mode != FLOW_MODE_HASH)
			if (mode != FLOW_MODE_HASH)
				goto err2;
				goto err3;
			perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
			perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
		}
		}
	} else {
	} else {
		err = -EINVAL;
		err = -EINVAL;
		if (!handle)
		if (!handle)
			goto err2;
			goto err3;
		if (!tb[TCA_FLOW_KEYS])
		if (!tb[TCA_FLOW_KEYS])
			goto err2;
			goto err3;


		mode = FLOW_MODE_MAP;
		mode = FLOW_MODE_MAP;
		if (tb[TCA_FLOW_MODE])
		if (tb[TCA_FLOW_MODE])
			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
		if (mode != FLOW_MODE_HASH && nkeys > 1)
		if (mode != FLOW_MODE_HASH && nkeys > 1)
			goto err2;
			goto err3;


		if (tb[TCA_FLOW_PERTURB]) {
		if (tb[TCA_FLOW_PERTURB]) {
			if (mode != FLOW_MODE_HASH)
			if (mode != FLOW_MODE_HASH)
				goto err2;
				goto err3;
			perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
			perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ;
		}
		}


@@ -542,6 +546,8 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
		call_rcu(&fold->rcu, flow_destroy_filter);
		call_rcu(&fold->rcu, flow_destroy_filter);
	return 0;
	return 0;


err3:
	tcf_exts_destroy(&fnew->exts);
err2:
err2:
	tcf_em_tree_destroy(&t);
	tcf_em_tree_destroy(&t);
	kfree(fnew);
	kfree(fnew);
Loading