netfilter: ebtables: split do_replace into two functions

once CONFIG_COMPAT support is merged this allows
to call do_replace_finish() after doing the CONFIG_COMPAT conversion
instead of copy & pasting this.

Signed-off-by: Florian Westphal <fw@strlen.de>
This commit is contained in:
Florian Westphal 2010-02-04 18:38:53 +01:00
parent 3e5e524ffb
commit e788759f44

View file

@ -959,91 +959,45 @@ static void get_counters(const struct ebt_counter *oldcounters,
} }
} }
/* replace the table */ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
static int do_replace(struct net *net, const void __user *user, struct ebt_table_info *newinfo)
unsigned int len)
{ {
int ret, i, countersize; int ret, i;
struct ebt_table_info *newinfo;
struct ebt_replace tmp;
struct ebt_table *t;
struct ebt_counter *counterstmp = NULL; struct ebt_counter *counterstmp = NULL;
/* used to be able to unlock earlier */ /* used to be able to unlock earlier */
struct ebt_table_info *table; struct ebt_table_info *table;
struct ebt_table *t;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
if (len != sizeof(tmp) + tmp.entries_size) {
BUGPRINT("Wrong len argument\n");
return -EINVAL;
}
if (tmp.entries_size == 0) {
BUGPRINT("Entries_size never zero\n");
return -EINVAL;
}
/* overflow check */
if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) / NR_CPUS -
SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
return -ENOMEM;
if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
return -ENOMEM;
countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
if (!newinfo)
return -ENOMEM;
if (countersize)
memset(newinfo->counters, 0, countersize);
newinfo->entries = vmalloc(tmp.entries_size);
if (!newinfo->entries) {
ret = -ENOMEM;
goto free_newinfo;
}
if (copy_from_user(
newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
BUGPRINT("Couldn't copy entries from userspace\n");
ret = -EFAULT;
goto free_entries;
}
/* the user wants counters back /* the user wants counters back
the check on the size is done later, when we have the lock */ the check on the size is done later, when we have the lock */
if (tmp.num_counters) { if (repl->num_counters) {
counterstmp = vmalloc(tmp.num_counters * sizeof(*counterstmp)); unsigned long size = repl->num_counters * sizeof(*counterstmp);
if (!counterstmp) { counterstmp = vmalloc(size);
ret = -ENOMEM; if (!counterstmp)
goto free_entries; return -ENOMEM;
}
} }
else
counterstmp = NULL;
/* this can get initialized by translate_table() */
newinfo->chainstack = NULL; newinfo->chainstack = NULL;
ret = ebt_verify_pointers(&tmp, newinfo); ret = ebt_verify_pointers(repl, newinfo);
if (ret != 0) if (ret != 0)
goto free_counterstmp; goto free_counterstmp;
ret = translate_table(net, tmp.name, newinfo); ret = translate_table(net, repl->name, newinfo);
if (ret != 0) if (ret != 0)
goto free_counterstmp; goto free_counterstmp;
t = find_table_lock(net, tmp.name, &ret, &ebt_mutex); t = find_table_lock(net, repl->name, &ret, &ebt_mutex);
if (!t) { if (!t) {
ret = -ENOENT; ret = -ENOENT;
goto free_iterate; goto free_iterate;
} }
/* the table doesn't like it */ /* the table doesn't like it */
if (t->check && (ret = t->check(newinfo, tmp.valid_hooks))) if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
goto free_unlock; goto free_unlock;
if (tmp.num_counters && tmp.num_counters != t->private->nentries) { if (repl->num_counters && repl->num_counters != t->private->nentries) {
BUGPRINT("Wrong nr. of counters requested\n"); BUGPRINT("Wrong nr. of counters requested\n");
ret = -EINVAL; ret = -EINVAL;
goto free_unlock; goto free_unlock;
@ -1059,7 +1013,7 @@ static int do_replace(struct net *net, const void __user *user,
module_put(t->me); module_put(t->me);
/* we need an atomic snapshot of the counters */ /* we need an atomic snapshot of the counters */
write_lock_bh(&t->lock); write_lock_bh(&t->lock);
if (tmp.num_counters) if (repl->num_counters)
get_counters(t->private->counters, counterstmp, get_counters(t->private->counters, counterstmp,
t->private->nentries); t->private->nentries);
@ -1070,10 +1024,9 @@ static int do_replace(struct net *net, const void __user *user,
allocation. Only reason why this is done is because this way the lock allocation. Only reason why this is done is because this way the lock
is held only once, while this doesn't bring the kernel into a is held only once, while this doesn't bring the kernel into a
dangerous state. */ dangerous state. */
if (tmp.num_counters && if (repl->num_counters &&
copy_to_user(tmp.counters, counterstmp, copy_to_user(repl->counters, counterstmp,
tmp.num_counters * sizeof(struct ebt_counter))) { repl->num_counters * sizeof(struct ebt_counter))) {
BUGPRINT("Couldn't copy counters to userspace\n");
ret = -EFAULT; ret = -EFAULT;
} }
else else
@ -1107,6 +1060,59 @@ free_counterstmp:
vfree(newinfo->chainstack[i]); vfree(newinfo->chainstack[i]);
vfree(newinfo->chainstack); vfree(newinfo->chainstack);
} }
return ret;
}
/* replace the table */
static int do_replace(struct net *net, const void __user *user,
unsigned int len)
{
int ret, countersize;
struct ebt_table_info *newinfo;
struct ebt_replace tmp;
if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
return -EFAULT;
if (len != sizeof(tmp) + tmp.entries_size) {
BUGPRINT("Wrong len argument\n");
return -EINVAL;
}
if (tmp.entries_size == 0) {
BUGPRINT("Entries_size never zero\n");
return -EINVAL;
}
/* overflow check */
if (tmp.nentries >= ((INT_MAX - sizeof(struct ebt_table_info)) /
NR_CPUS - SMP_CACHE_BYTES) / sizeof(struct ebt_counter))
return -ENOMEM;
if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
return -ENOMEM;
countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
if (!newinfo)
return -ENOMEM;
if (countersize)
memset(newinfo->counters, 0, countersize);
newinfo->entries = vmalloc(tmp.entries_size);
if (!newinfo->entries) {
ret = -ENOMEM;
goto free_newinfo;
}
if (copy_from_user(
newinfo->entries, tmp.entries, tmp.entries_size) != 0) {
BUGPRINT("Couldn't copy entries from userspace\n");
ret = -EFAULT;
goto free_entries;
}
ret = do_replace_finish(net, &tmp, newinfo);
if (ret == 0)
return ret;
free_entries: free_entries:
vfree(newinfo->entries); vfree(newinfo->entries);
free_newinfo: free_newinfo: