Commit f2da974c authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[PKT_SCHED]: police action: fix multiple bugs in init path

- Return proper error codes
- Some attribute sizes are not checked
- rta may by NULL
- multiple leaks
- possible unbalanced unlock
- used action is freed after if an error happens while trying to replace
- estimator can't be replaced

This patch makes replacement atomic, so the old action is either
replaced entirely or not touched at all.
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ec93af8a
...@@ -165,20 +165,28 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, ...@@ -165,20 +165,28 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,
struct tc_action *a, int ovr, int bind) struct tc_action *a, int ovr, int bind)
{ {
unsigned h; unsigned h;
int ret = 0; int ret = 0, err;
struct rtattr *tb[TCA_POLICE_MAX]; struct rtattr *tb[TCA_POLICE_MAX];
struct tc_police *parm; struct tc_police *parm;
struct tcf_police *p; struct tcf_police *p;
struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL;
if (rtattr_parse(tb, TCA_POLICE_MAX, RTA_DATA(rta), if (rta == NULL || rtattr_parse(tb, TCA_POLICE_MAX, RTA_DATA(rta),
RTA_PAYLOAD(rta)) < 0) RTA_PAYLOAD(rta)) < 0)
return -1; return -EINVAL;
if (tb[TCA_POLICE_TBF-1] == NULL || if (tb[TCA_POLICE_TBF-1] == NULL ||
RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]) != sizeof(*parm)) RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]) != sizeof(*parm))
return -1; return -EINVAL;
parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); parm = RTA_DATA(tb[TCA_POLICE_TBF-1]);
if (tb[TCA_POLICE_RESULT-1] != NULL &&
RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32))
return -EINVAL;
if (tb[TCA_POLICE_RESULT-1] != NULL &&
RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32))
return -EINVAL;
if (parm->index && (p = tcf_police_lookup(parm->index)) != NULL) { if (parm->index && (p = tcf_police_lookup(parm->index)) != NULL) {
a->priv = p; a->priv = p;
spin_lock(&p->lock); spin_lock(&p->lock);
...@@ -186,18 +194,18 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, ...@@ -186,18 +194,18 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,
p->bindcnt += 1; p->bindcnt += 1;
p->refcnt += 1; p->refcnt += 1;
} }
spin_unlock(&p->lock);
if (ovr) if (ovr)
goto override; goto override;
spin_unlock(&p->lock);
return ret; return ret;
} }
p = kmalloc(sizeof(*p), GFP_KERNEL); p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL) if (p == NULL)
return -1; return -ENOMEM;
memset(p, 0, sizeof(*p)); memset(p, 0, sizeof(*p));
ret = 1; ret = ACT_P_CREATED;
p->refcnt = 1; p->refcnt = 1;
spin_lock_init(&p->lock); spin_lock_init(&p->lock);
p->stats_lock = &p->lock; p->stats_lock = &p->lock;
...@@ -205,28 +213,32 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, ...@@ -205,28 +213,32 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,
p->bindcnt = 1; p->bindcnt = 1;
override: override:
if (parm->rate.rate) { if (parm->rate.rate) {
p->R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); err = -ENOMEM;
if (p->R_tab == NULL) R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]);
if (R_tab == NULL)
goto failure; goto failure;
if (parm->peakrate.rate) { if (parm->peakrate.rate) {
p->P_tab = qdisc_get_rtab(&parm->peakrate, P_tab = qdisc_get_rtab(&parm->peakrate,
tb[TCA_POLICE_PEAKRATE-1]); tb[TCA_POLICE_PEAKRATE-1]);
if (p->P_tab == NULL) if (p->P_tab == NULL) {
qdisc_put_rtab(R_tab);
goto failure; goto failure;
} }
} }
if (tb[TCA_POLICE_RESULT-1]) {
if (RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32))
goto failure;
p->result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]);
} }
#ifdef CONFIG_NET_ESTIMATOR /* No failure allowed after this point */
if (tb[TCA_POLICE_AVRATE-1]) { spin_lock(&p->lock);
if (RTA_PAYLOAD(tb[TCA_POLICE_AVRATE-1]) != sizeof(u32)) if (R_tab != NULL) {
goto failure; qdisc_put_rtab(p->R_tab);
p->ewma_rate = *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); p->R_tab = R_tab;
} }
#endif if (P_tab != NULL) {
qdisc_put_rtab(p->P_tab);
p->P_tab = P_tab;
}
if (tb[TCA_POLICE_RESULT-1])
p->result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]);
p->toks = p->burst = parm->burst; p->toks = p->burst = parm->burst;
p->mtu = parm->mtu; p->mtu = parm->mtu;
if (p->mtu == 0) { if (p->mtu == 0) {
...@@ -238,16 +250,19 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, ...@@ -238,16 +250,19 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,
p->ptoks = L2T_P(p, p->mtu); p->ptoks = L2T_P(p, p->mtu);
p->action = parm->action; p->action = parm->action;
if (ovr) { #ifdef CONFIG_NET_ESTIMATOR
if (tb[TCA_POLICE_AVRATE-1])
p->ewma_rate = *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]);
if (est)
gen_replace_estimator(&p->bstats, &p->rate_est, p->stats_lock, est);
#endif
spin_unlock(&p->lock); spin_unlock(&p->lock);
if (ret != ACT_P_CREATED)
return ret; return ret;
}
PSCHED_GET_TIME(p->t_c); PSCHED_GET_TIME(p->t_c);
p->index = parm->index ? : tcf_police_new_index(); p->index = parm->index ? : tcf_police_new_index();
#ifdef CONFIG_NET_ESTIMATOR
if (est)
gen_new_estimator(&p->bstats, &p->rate_est, p->stats_lock, est);
#endif
h = tcf_police_hash(p->index); h = tcf_police_hash(p->index);
write_lock_bh(&police_lock); write_lock_bh(&police_lock);
p->next = tcf_police_ht[h]; p->next = tcf_police_ht[h];
...@@ -258,12 +273,9 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, ...@@ -258,12 +273,9 @@ static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est,
return ret; return ret;
failure: failure:
if (p->R_tab) if (ret == ACT_P_CREATED)
qdisc_put_rtab(p->R_tab);
if (ovr)
spin_unlock(&p->lock);
kfree(p); kfree(p);
return -1; return err;
} }
static int tcf_act_police_cleanup(struct tc_action *a, int bind) static int tcf_act_police_cleanup(struct tc_action *a, int bind)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment