Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e788759f authored by Florian Westphal's avatar Florian Westphal
Browse files

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: default avatarFlorian Westphal <fw@strlen.de>
parent 3e5e524f
Loading
Loading
Loading
Loading
+71 −65
Original line number Diff line number Diff line
@@ -959,91 +959,45 @@ static void get_counters(const struct ebt_counter *oldcounters,
	}
}

/* replace the table */
static int do_replace(struct net *net, const void __user *user,
		      unsigned int len)
static int do_replace_finish(struct net *net, struct ebt_replace *repl,
			      struct ebt_table_info *newinfo)
{
	int ret, i, countersize;
	struct ebt_table_info *newinfo;
	struct ebt_replace tmp;
	struct ebt_table *t;
	int ret, i;
	struct ebt_counter *counterstmp = NULL;
	/* used to be able to unlock earlier */
	struct ebt_table_info *table;

	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;
	}
	struct ebt_table *t;

	/* the user wants counters back
	   the check on the size is done later, when we have the lock */
	if (tmp.num_counters) {
		counterstmp = vmalloc(tmp.num_counters * sizeof(*counterstmp));
		if (!counterstmp) {
			ret = -ENOMEM;
			goto free_entries;
		}
	if (repl->num_counters) {
		unsigned long size = repl->num_counters * sizeof(*counterstmp);
		counterstmp = vmalloc(size);
		if (!counterstmp)
			return -ENOMEM;
	}
	else
		counterstmp = NULL;

	/* this can get initialized by translate_table() */
	newinfo->chainstack = NULL;
	ret = ebt_verify_pointers(&tmp, newinfo);
	ret = ebt_verify_pointers(repl, newinfo);
	if (ret != 0)
		goto free_counterstmp;

	ret = translate_table(net, tmp.name, newinfo);
	ret = translate_table(net, repl->name, newinfo);

	if (ret != 0)
		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) {
		ret = -ENOENT;
		goto free_iterate;
	}

	/* 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;

	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");
		ret = -EINVAL;
		goto free_unlock;
@@ -1059,7 +1013,7 @@ static int do_replace(struct net *net, const void __user *user,
		module_put(t->me);
	/* we need an atomic snapshot of the counters */
	write_lock_bh(&t->lock);
	if (tmp.num_counters)
	if (repl->num_counters)
		get_counters(t->private->counters, counterstmp,
		   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
	   is held only once, while this doesn't bring the kernel into a
	   dangerous state. */
	if (tmp.num_counters &&
	   copy_to_user(tmp.counters, counterstmp,
	   tmp.num_counters * sizeof(struct ebt_counter))) {
		BUGPRINT("Couldn't copy counters to userspace\n");
	if (repl->num_counters &&
	   copy_to_user(repl->counters, counterstmp,
	   repl->num_counters * sizeof(struct ebt_counter))) {
		ret = -EFAULT;
	}
	else
@@ -1107,6 +1060,59 @@ static int do_replace(struct net *net, const void __user *user,
			vfree(newinfo->chainstack[i]);
		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:
	vfree(newinfo->entries);
free_newinfo: