CVE-2011-4611: Numeric Errors (CWE-189)
value: Integer overflow in the perf_event_interrupt function in arch/powerpc/kernel/perf_event.c in the Linux kernel before 2.6.39 on powerpc platforms allows local users to cause a denial of service (unhandled performance monitor exception) via vectors that trigger certain outcomes of performance events.
CWE description: Weaknesses in this category are related to improper calculation or conversion of numbers.
Published: 2012-05-17T11:00Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: arch/powerpc/include/asm/reg.h, arch/powerpc/kernel/perf_event.c
Commit date: 2011-03-16 14:04:13 +0100
Commit message: perf, powerpc: Handle events that raise an exception without overflowing

Events on POWER7 can roll back if a speculative event doesn't
eventually complete. Unfortunately in some rare cases they will
raise a performance monitor exception. We need to catch this to
ensure we reset the PMC. In all cases the PMC will be 256 or less
cycles from overflow.

Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: <stable@kernel.org> # as far back as it applies cleanly
LKML-Reference: <20110309143842.6c22845e@kryten>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

Diff: diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 125fc1ad665d0..7626fa78e1f8b 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -878,10 +878,11 @@
 #define PV_SSTAR	0x0037
 #define PV_POWER4p	0x0038
 #define PV_970		0x0039
 #define PV_POWER5	0x003A
 #define PV_POWER5p	0x003B
+#define PV_POWER7	0x003F
 #define PV_970FX	0x003C
 #define PV_630		0x0040
 #define PV_630p	0x0041
 #define PV_970MP	0x0044
 #define PV_970GX	0x0045
diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c
index ab6f6beadb572..97e0ae414940e 100644
--- a/arch/powerpc/kernel/perf_event.c
+++ b/arch/powerpc/kernel/perf_event.c
@@ -1267,10 +1267,32 @@ unsigned long perf_instruction_pointer(struct pt_regs *regs)
 
 	ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs);
 	return ip;
 }
 
+static bool pmc_overflow(unsigned long val)
+{
+	if ((int)val < 0)
+		return true;
+
+	/*
+	 * Events on POWER7 can roll back if a speculative event doesn't
+	 * eventually complete. Unfortunately in some rare cases they will
+	 * raise a performance monitor exception. We need to catch this to
+	 * ensure we reset the PMC. In all cases the PMC will be 256 or less
+	 * cycles from overflow.
+	 *
+	 * We only do this if the first pass fails to find any overflowing
+	 * PMCs because a user might set a period of less than 256 and we
+	 * don't want to mistakenly reset them.
+	 */
+	if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256))
+		return true;
+
+	return false;
+}
+
 /*
  * Performance monitor interrupt stuff
  */
 static void perf_event_interrupt(struct pt_regs *regs)
 {
@@ -1314,11 +1336,11 @@ static void perf_event_interrupt(struct pt_regs *regs)
 	if (!found) {
 		for (i = 0; i < ppmu->n_counter; ++i) {
 			if (is_limited_pmc(i + 1))
 				continue;
 			val = read_pmc(i + 1);
-			if ((int)val < 0)
+			if (pmc_overflow(val))
 				write_pmc(i + 1, 0);
 		}
 	}
 
 	/*

Vulnerable code: 			if ((int)val < 0)
Fixed code: #define PV_POWER7	0x003F
static bool pmc_overflow(unsigned long val)
{
	if ((int)val < 0)
		return true;

	/*
	 * Events on POWER7 can roll back if a speculative event doesn't
	 * eventually complete. Unfortunately in some rare cases they will
	 * raise a performance monitor exception. We need to catch this to
	 * ensure we reset the PMC. In all cases the PMC will be 256 or less
	 * cycles from overflow.
	 *
	 * We only do this if the first pass fails to find any overflowing
	 * PMCs because a user might set a period of less than 256 and we
	 * don't want to mistakenly reset them.
	 */
	if (__is_processor(PV_POWER7) && ((0x80000000 - val) <= 256))
		return true;

	return false;
}

			if (pmc_overflow(val))