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 Diff line number Diff line
@@ -69,17 +69,19 @@ struct tcf_exts {
	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
	exts->type = 0;
	exts->nr_actions = 0;
	exts->actions = kcalloc(TCA_ACT_MAX_PRIO, sizeof(struct tc_action *),
				GFP_KERNEL);
	WARN_ON(!exts->actions); /* TODO: propagate the error to callers */
	if (!exts->actions)
		return -ENOMEM;
#endif
	exts->action = action;
	exts->police = police;
	return 0;
}

/**
+9 −3
Original line number 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_ematch_tree t;

	tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	err = tcf_exts_validate(net, tp, tb, est, &e, ovr);
	err = tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
	if (err < 0)
		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);
	if (err < 0)
@@ -189,7 +191,10 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
	if (!fnew)
		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;
	if (handle) {
		fnew->handle = handle;
@@ -226,6 +231,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,

	return 0;
errout:
	tcf_exts_destroy(&fnew->exts);
	kfree(fnew);
	return err;
}
+17 −10
Original line number 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))
		return -EINVAL;

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

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

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

		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) :
		       cls_bpf_prog_from_efd(tb, prog, tp);
	if (ret < 0) {
		tcf_exts_destroy(&exts);
		return ret;
	}
	if (ret < 0)
		goto errout;

	if (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);
	return 0;

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

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)
		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 (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;
	return 0;

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

	return ret;
}

+10 −3
Original line number Diff line number Diff line
@@ -93,7 +93,9 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
	if (!new)
		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->tp = tp;
	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)
		goto errout;

	tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
	err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr);
	err = tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
	if (err < 0)
		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);
	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);
	return 0;
errout:
	tcf_exts_destroy(&new->exts);
	kfree(new);
	return err;
}
+16 −10
Original line number Diff line number Diff line
@@ -418,10 +418,12 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
			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);
	if (err < 0)
		return err;
		goto err1;

	err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &t);
	if (err < 0)
@@ -432,13 +434,15 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
	if (!fnew)
		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;
	if (fold) {
		err = -EINVAL;
		if (fold->handle != handle && handle)
			goto err2;
			goto err3;

		/* Copy fold into fnew */
		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])
			mode = nla_get_u32(tb[TCA_FLOW_MODE]);
		if (mode != FLOW_MODE_HASH && nkeys > 1)
			goto err2;
			goto err3;

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

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

		if (tb[TCA_FLOW_PERTURB]) {
			if (mode != FLOW_MODE_HASH)
				goto err2;
				goto err3;
			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);
	return 0;

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