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

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

- Return proper error codes
- Attribute sizes are not checked
- rta may by NULL
- The action is inserted into the hash before its parameters are set
- action in hash is freed on error path
- action is modified outside of the locked section

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 19c29ed5
...@@ -81,36 +81,22 @@ tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, ...@@ -81,36 +81,22 @@ tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,
struct tc_mirred *parm; struct tc_mirred *parm;
struct tcf_mirred *p; struct tcf_mirred *p;
struct net_device *dev = NULL; struct net_device *dev = NULL;
int size = sizeof(*p), new = 0; int ret = 0;
int ok_push = 0;
if (rtattr_parse(tb, TCA_MIRRED_MAX, RTA_DATA(rta), if (rta == NULL || rtattr_parse(tb, TCA_MIRRED_MAX, RTA_DATA(rta),
RTA_PAYLOAD(rta)) < 0) { RTA_PAYLOAD(rta)) < 0)
DPRINTK("tcf_mirred_init BUG in user space couldnt parse " return -EINVAL;
"properly\n");
return -1;
}
if (tb[TCA_MIRRED_PARMS - 1] == NULL) {
DPRINTK("BUG: tcf_mirred_init called with NULL params\n");
return -1;
}
parm = RTA_DATA(tb[TCA_MIRRED_PARMS - 1]); if (tb[TCA_MIRRED_PARMS-1] == NULL ||
RTA_PAYLOAD(tb[TCA_MIRRED_PARMS-1]) < sizeof(*parm))
p = tcf_hash_check(parm, a, ovr, bind); return -EINVAL;
if (p == NULL) { /* new */ parm = RTA_DATA(tb[TCA_MIRRED_PARMS-1]);
p = tcf_hash_create(parm, est, a, size, ovr, bind);
if (p == NULL)
return -1;
new = 1;
}
if (parm->ifindex) { if (parm->ifindex) {
dev = dev_get_by_index(parm->ifindex); dev = __dev_get_by_index(parm->ifindex);
if (dev == NULL) { if (dev == NULL)
printk("BUG: tcf_mirred_init called with bad device\n"); return -ENODEV;
return -1;
}
switch (dev->type) { switch (dev->type) {
case ARPHRD_TUNNEL: case ARPHRD_TUNNEL:
case ARPHRD_TUNNEL6: case ARPHRD_TUNNEL6:
...@@ -118,36 +104,48 @@ tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, ...@@ -118,36 +104,48 @@ tcf_mirred_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,
case ARPHRD_IPGRE: case ARPHRD_IPGRE:
case ARPHRD_VOID: case ARPHRD_VOID:
case ARPHRD_NONE: case ARPHRD_NONE:
p->ok_push = 0; ok_push = 0;
break; break;
default: default:
p->ok_push = 1; ok_push = 1;
break; break;
} }
} else {
if (new) {
kfree(p);
return -1;
}
} }
if (new || ovr) { p = tcf_hash_check(parm->index, a, ovr, bind);
spin_lock(&p->lock); if (p == NULL) {
p->action = parm->action; if (!parm->ifindex)
p->eaction = parm->eaction; return -EINVAL;
if (parm->ifindex) { p = tcf_hash_create(parm->index, est, a, sizeof(*p), ovr, bind);
p->ifindex = parm->ifindex; if (p == NULL)
if (ovr) return -ENOMEM;
dev_put(p->dev); ret = ACT_P_CREATED;
p->dev = dev; } else {
if (!ovr) {
tcf_mirred_release(p, bind);
return -EEXIST;
} }
spin_unlock(&p->lock);
} }
spin_lock(&p->lock);
p->action = parm->action;
p->eaction = parm->eaction;
if (parm->ifindex) {
p->ifindex = parm->ifindex;
if (ret != ACT_P_CREATED)
dev_put(p->dev);
p->dev = dev;
dev_hold(dev);
p->ok_push = ok_push;
}
spin_unlock(&p->lock);
if (ret == ACT_P_CREATED)
tcf_hash_insert(p);
DPRINTK("tcf_mirred_init index %d action %d eaction %d device %s " DPRINTK("tcf_mirred_init index %d action %d eaction %d device %s "
"ifindex %d\n", parm->index, parm->action, parm->eaction, "ifindex %d\n", parm->index, parm->action, parm->eaction,
dev->name, parm->ifindex); dev->name, parm->ifindex);
return new; return ret;
} }
static int static int
......
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