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

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

- Return proper error codes
- Attribute sizes are not checked
- rta may be NULL
- Several leaks and locking errors
- When replacement fails the old action is freed while in use

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 24b2c435
...@@ -53,29 +53,27 @@ static rwlock_t ipt_lock = RW_LOCK_UNLOCKED; ...@@ -53,29 +53,27 @@ static rwlock_t ipt_lock = RW_LOCK_UNLOCKED;
#define tcf_t_lock ipt_lock #define tcf_t_lock ipt_lock
#define tcf_ht tcf_ipt_ht #define tcf_ht tcf_ipt_ht
#define CONFIG_NET_ACT_INIT
#include <net/pkt_act.h> #include <net/pkt_act.h>
static inline int static int
init_targ(struct tcf_ipt *p) ipt_init_target(struct ipt_entry_target *t, char *table, unsigned int hook)
{ {
struct ipt_target *target; struct ipt_target *target;
int ret = 0; int ret = 0;
struct ipt_entry_target *t = p->t;
target = ipt_find_target(t->u.user.name, t->u.user.revision); target = ipt_find_target(t->u.user.name, t->u.user.revision);
if (!target) { if (!target)
printk("init_targ: Failed to find %s\n", t->u.user.name); return -ENOENT;
return -1;
}
DPRINTK("init_targ: found %s\n", target->name); DPRINTK("ipt_init_target: found %s\n", target->name);
t->u.kernel.target = target; t->u.kernel.target = target;
if (t->u.kernel.target->checkentry if (t->u.kernel.target->checkentry
&& !t->u.kernel.target->checkentry(p->tname, NULL, t->data, && !t->u.kernel.target->checkentry(table, NULL, t->data,
t->u.target_size - sizeof(*t), t->u.target_size - sizeof(*t),
p->hook)) { hook)) {
DPRINTK("ip_tables: check failed for `%s'.\n", DPRINTK("ipt_init_target: check failed for `%s'.\n",
t->u.kernel.target->name); t->u.kernel.target->name);
module_put(t->u.kernel.target->me); module_put(t->u.kernel.target->me);
ret = -EINVAL; ret = -EINVAL;
...@@ -84,137 +82,97 @@ init_targ(struct tcf_ipt *p) ...@@ -84,137 +82,97 @@ init_targ(struct tcf_ipt *p)
return ret; return ret;
} }
static void
ipt_destroy_target(struct ipt_entry_target *t)
{
if (t->u.kernel.target->destroy)
t->u.kernel.target->destroy(t->data,
t->u.target_size - sizeof(*t));
module_put(t->u.kernel.target->me);
}
static int static int
tcf_ipt_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a, tcf_ipt_init(struct rtattr *rta, struct rtattr *est, struct tc_action *a,
int ovr, int bind) int ovr, int bind)
{ {
struct ipt_entry_target *t;
unsigned h;
struct rtattr *tb[TCA_IPT_MAX]; struct rtattr *tb[TCA_IPT_MAX];
struct tcf_ipt *p; struct tcf_ipt *p;
int ret = 0; struct ipt_entry_target *td, *t;
u32 index = 0; char *tname;
int ret = 0, err;
u32 hook = 0; u32 hook = 0;
u32 index = 0;
if (rta == NULL || if (rta == NULL ||
rtattr_parse(tb, TCA_IPT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0) rtattr_parse(tb, TCA_IPT_MAX, RTA_DATA(rta), RTA_PAYLOAD(rta)) < 0)
return -1; return -EINVAL;
if (tb[TCA_IPT_INDEX - 1]) { if (tb[TCA_IPT_HOOK-1] == NULL ||
index = *(u32 *) RTA_DATA(tb[TCA_IPT_INDEX - 1]); RTA_PAYLOAD(tb[TCA_IPT_HOOK-1]) < sizeof(u32))
DPRINTK("ipt index %d\n", index); return -EINVAL;
} if (tb[TCA_IPT_TARG-1] == NULL ||
RTA_PAYLOAD(tb[TCA_IPT_TARG-1]) < sizeof(*t))
if (index && (p = tcf_hash_lookup(index)) != NULL) { return -EINVAL;
a->priv = (void *) p; td = (struct ipt_entry_target *)RTA_DATA(tb[TCA_IPT_TARG-1]);
spin_lock(&p->lock); if (RTA_PAYLOAD(tb[TCA_IPT_TARG-1]) < td->u.target_size)
if (bind) { return -EINVAL;
p->bindcnt += 1;
p->refcnt += 1; if (tb[TCA_IPT_INDEX-1] != NULL &&
RTA_PAYLOAD(tb[TCA_IPT_INDEX-1]) >= sizeof(u32))
index = *(u32 *)RTA_DATA(tb[TCA_IPT_INDEX-1]);
p = tcf_hash_check(index, a, ovr, bind);
if (p == NULL) {
p = tcf_hash_create(index, est, a, sizeof(*p), ovr, bind);
if (p == NULL)
return -ENOMEM;
ret = ACT_P_CREATED;
} else {
if (!ovr) {
tcf_hash_release(p, bind);
return -EEXIST;
} }
if (ovr)
goto override;
spin_unlock(&p->lock);
return ret;
} }
if (tb[TCA_IPT_TARG - 1] == NULL || tb[TCA_IPT_HOOK - 1] == NULL) hook = *(u32 *)RTA_DATA(tb[TCA_IPT_HOOK-1]);
return -1;
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return -1;
memset(p, 0, sizeof(*p));
p->refcnt = 1;
ret = 1;
spin_lock_init(&p->lock);
p->stats_lock = &p->lock;
if (bind)
p->bindcnt = 1;
override:
hook = *(u32 *) RTA_DATA(tb[TCA_IPT_HOOK - 1]);
t = (struct ipt_entry_target *) RTA_DATA(tb[TCA_IPT_TARG - 1]);
p->t = kmalloc(t->u.target_size, GFP_KERNEL);
if (p->t == NULL) {
if (ovr) {
printk("ipt policy messed up \n");
spin_unlock(&p->lock);
return -1;
}
kfree(p);
return -1;
}
memcpy(p->t, RTA_DATA(tb[TCA_IPT_TARG - 1]), t->u.target_size); err = -ENOMEM;
DPRINTK(" target NAME %s size %d data[0] %x data[1] %x\n", tname = kmalloc(IFNAMSIZ, GFP_KERNEL);
t->u.user.name, t->u.target_size, t->data[0], t->data[1]); if (tname == NULL)
goto err1;
if (tb[TCA_IPT_TABLE - 1] == NULL ||
rtattr_strlcpy(tname, tb[TCA_IPT_TABLE-1], IFNAMSIZ) >= IFNAMSIZ)
strcpy(tname, "mangle");
p->tname = kmalloc(IFNAMSIZ, GFP_KERNEL); t = kmalloc(td->u.target_size, GFP_KERNEL);
if (t == NULL)
goto err2;
memcpy(t, td, td->u.target_size);
if (p->tname == NULL) { if ((err = ipt_init_target(t, tname, hook)) < 0)
if (ovr) { goto err3;
printk("ipt policy messed up 2 \n");
spin_unlock(&p->lock);
return -1;
}
kfree(p->t);
kfree(p);
return -1;
} else {
int csize = IFNAMSIZ - 1;
memset(p->tname, 0, IFNAMSIZ);
if (tb[TCA_IPT_TABLE - 1]) {
if (strlen((char *) RTA_DATA(tb[TCA_IPT_TABLE - 1])) <
csize)
csize = strlen(RTA_DATA(tb[TCA_IPT_TABLE - 1]));
strncpy(p->tname, RTA_DATA(tb[TCA_IPT_TABLE - 1]),
csize);
DPRINTK("table name %s\n", p->tname);
} else {
strncpy(p->tname, "mangle", 1 + strlen("mangle"));
}
}
if (init_targ(p) < 0) { spin_lock_bh(&p->lock);
if (ovr) { if (ret != ACT_P_CREATED) {
printk("ipt policy messed up 2 \n"); ipt_destroy_target(p->t);
spin_unlock(&p->lock);
return -1;
}
kfree(p->tname); kfree(p->tname);
kfree(p->t); kfree(p->t);
kfree(p);
return -1;
}
if (ovr) {
spin_unlock(&p->lock);
return -1;
} }
p->tname = tname;
p->index = index ? : tcf_hash_new_index(); p->t = t;
p->hook = hook;
p->tm.lastuse = jiffies; spin_unlock_bh(&p->lock);
/* if (ret == ACT_P_CREATED)
p->tm.expires = jiffies; tcf_hash_insert(p);
*/
p->tm.install = jiffies;
#ifdef CONFIG_NET_ESTIMATOR
if (est)
gen_new_estimator(&p->bstats, &p->rate_est, p->stats_lock, est);
#endif
h = tcf_hash(p->index);
write_lock_bh(&ipt_lock);
p->next = tcf_ipt_ht[h];
tcf_ipt_ht[h] = p;
write_unlock_bh(&ipt_lock);
a->priv = p;
return ret; return ret;
err3:
kfree(t);
err2:
kfree(tname);
err1:
kfree(p);
return err;
} }
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