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

Commit c8923c6b authored by David S. Miller's avatar David S. Miller
Browse files

[NETFILTER]: Fix OOPSes on machines with discontiguous cpu numbering.



Original patch by Harald Welte, with feedback from Herbert Xu
and testing by Sébastien Bernard.

EBTABLES, ARP tables, and IP/IP6 tables all assume that cpus
are numbered linearly.  That is not necessarily true.

This patch fixes that up by calculating the largest possible
cpu number, and allocating enough per-cpu structure space given
that.

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c931488c
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
#include <linux/module.h>

#define IPI_SCHEDULE 1
#define IPI_CALL 2
@@ -28,6 +29,7 @@ spinlock_t cris_atomic_locks[] = { [0 ... LOCK_COUNT - 1] = SPIN_LOCK_UNLOCKED};
/* CPU masks */
cpumask_t cpu_online_map = CPU_MASK_NONE;
cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
EXPORT_SYMBOL(phys_cpu_present_map);

/* Variables used during SMP boot */
volatile int cpu_now_booting = 0;
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/sched.h>
#include <linux/module.h>

#include <asm/atomic.h>
#include <asm/processor.h>
@@ -39,6 +40,8 @@ struct sh_cpuinfo cpu_data[NR_CPUS];
extern void per_cpu_trap_init(void);

cpumask_t cpu_possible_map;
EXPORT_SYMBOL(cpu_possible_map);

cpumask_t cpu_online_map;
static atomic_t cpus_booted = ATOMIC_INIT(0);

+12 −0
Original line number Diff line number Diff line
@@ -392,4 +392,16 @@ extern cpumask_t cpu_present_map;
#define for_each_online_cpu(cpu)  for_each_cpu_mask((cpu), cpu_online_map)
#define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map)

/* Find the highest possible smp_processor_id() */
static inline unsigned int highest_possible_processor_id(void)
{
	unsigned int cpu, highest = 0;

	for_each_cpu_mask(cpu, cpu_possible_map)
		highest = cpu;

	return highest;
}


#endif /* __LINUX_CPUMASK_H */
+17 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <linux/spinlock.h>
#include <asm/uaccess.h>
#include <linux/smp.h>
#include <linux/cpumask.h>
#include <net/sock.h>
/* needed for logical [in,out]-dev filtering */
#include "../br_private.h"
@@ -823,10 +824,11 @@ static int translate_table(struct ebt_replace *repl,
		/* this will get free'd in do_replace()/ebt_register_table()
		   if an error occurs */
		newinfo->chainstack = (struct ebt_chainstack **)
		   vmalloc(num_possible_cpus() * sizeof(struct ebt_chainstack));
		   vmalloc((highest_possible_processor_id()+1) 
				   		* sizeof(struct ebt_chainstack));
		if (!newinfo->chainstack)
			return -ENOMEM;
		for (i = 0; i < num_possible_cpus(); i++) {
		for_each_cpu(i) {
			newinfo->chainstack[i] =
			   vmalloc(udc_cnt * sizeof(struct ebt_chainstack));
			if (!newinfo->chainstack[i]) {
@@ -896,8 +898,11 @@ static void get_counters(struct ebt_counter *oldcounters,
	/* counters of cpu 0 */
	memcpy(counters, oldcounters,
	       sizeof(struct ebt_counter) * nentries);

	/* add other counters to those of cpu 0 */
	for (cpu = 1; cpu < num_possible_cpus(); cpu++) {
	for_each_cpu(cpu) {
		if (cpu == 0)
			continue;
		counter_base = COUNTER_BASE(oldcounters, nentries, cpu);
		for (i = 0; i < nentries; i++) {
			counters[i].pcnt += counter_base[i].pcnt;
@@ -929,7 +934,8 @@ static int do_replace(void __user *user, unsigned int len)
		BUGPRINT("Entries_size never zero\n");
		return -EINVAL;
	}
	countersize = COUNTER_OFFSET(tmp.nentries) * num_possible_cpus();
	countersize = COUNTER_OFFSET(tmp.nentries) * 
					(highest_possible_processor_id()+1);
	newinfo = (struct ebt_table_info *)
	   vmalloc(sizeof(struct ebt_table_info) + countersize);
	if (!newinfo)
@@ -1022,7 +1028,7 @@ static int do_replace(void __user *user, unsigned int len)

	vfree(table->entries);
	if (table->chainstack) {
		for (i = 0; i < num_possible_cpus(); i++)
		for_each_cpu(i)
			vfree(table->chainstack[i]);
		vfree(table->chainstack);
	}
@@ -1040,7 +1046,7 @@ static int do_replace(void __user *user, unsigned int len)
	vfree(counterstmp);
	/* can be initialized in translate_table() */
	if (newinfo->chainstack) {
		for (i = 0; i < num_possible_cpus(); i++)
		for_each_cpu(i)
			vfree(newinfo->chainstack[i]);
		vfree(newinfo->chainstack);
	}
@@ -1132,7 +1138,8 @@ int ebt_register_table(struct ebt_table *table)
		return -EINVAL;
	}

	countersize = COUNTER_OFFSET(table->table->nentries) * num_possible_cpus();
	countersize = COUNTER_OFFSET(table->table->nentries) *
					(highest_possible_processor_id()+1);
	newinfo = (struct ebt_table_info *)
	   vmalloc(sizeof(struct ebt_table_info) + countersize);
	ret = -ENOMEM;
@@ -1186,7 +1193,7 @@ int ebt_register_table(struct ebt_table *table)
	up(&ebt_mutex);
free_chainstack:
	if (newinfo->chainstack) {
		for (i = 0; i < num_possible_cpus(); i++)
		for_each_cpu(i)
			vfree(newinfo->chainstack[i]);
		vfree(newinfo->chainstack);
	}
@@ -1209,7 +1216,7 @@ void ebt_unregister_table(struct ebt_table *table)
	up(&ebt_mutex);
	vfree(table->private->entries);
	if (table->private->chainstack) {
		for (i = 0; i < num_possible_cpus(); i++)
		for_each_cpu(i)
			vfree(table->private->chainstack[i]);
		vfree(table->private->chainstack);
	}
+9 −5
Original line number Diff line number Diff line
@@ -716,7 +716,9 @@ static int translate_table(const char *name,
	}

	/* And one copy for every other CPU */
	for (i = 1; i < num_possible_cpus(); i++) {
	for_each_cpu(i) {
		if (i == 0)
			continue;
		memcpy(newinfo->entries + SMP_ALIGN(newinfo->size) * i,
		       newinfo->entries,
		       SMP_ALIGN(newinfo->size));
@@ -767,7 +769,7 @@ static void get_counters(const struct arpt_table_info *t,
	unsigned int cpu;
	unsigned int i;

	for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
	for_each_cpu(cpu) {
		i = 0;
		ARPT_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
				   t->size,
@@ -885,7 +887,8 @@ static int do_replace(void __user *user, unsigned int len)
		return -ENOMEM;

	newinfo = vmalloc(sizeof(struct arpt_table_info)
			  + SMP_ALIGN(tmp.size) * num_possible_cpus());
			  + SMP_ALIGN(tmp.size) *
			  		(highest_possible_processor_id()+1));
	if (!newinfo)
		return -ENOMEM;

@@ -1158,7 +1161,8 @@ int arpt_register_table(struct arpt_table *table,
		= { 0, 0, 0, { 0 }, { 0 }, { } };

	newinfo = vmalloc(sizeof(struct arpt_table_info)
			  + SMP_ALIGN(repl->size) * num_possible_cpus());
			  + SMP_ALIGN(repl->size) *
			  		(highest_possible_processor_id()+1));
	if (!newinfo) {
		ret = -ENOMEM;
		return ret;
Loading