CVE-2011-2918 (C, published 2012-05-24T23:55Z)
Description: value: The Performance Events subsystem in the Linux kernel before 3.1 does not properly handle event overflows associated with PERF_COUNT_SW_CPU_CLOCK events, which allows local users to cause a denial of service (system hang) via a crafted application.
Severity: MEDIUM (CVSS v3 base score: 5.5)
Weakness: CWE-400 \u2014 Uncontrolled Resource Consumption
The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
Repository: https://github.com/torvalds/linux
Files changed:
arch/alpha/kernel/perf_event.c
arch/arm/kernel/perf_event_v6.c
arch/arm/kernel/perf_event_v7.c
arch/arm/kernel/perf_event_xscale.c
arch/arm/kernel/ptrace.c
arch/arm/kernel/swp_emulate.c
arch/arm/mm/fault.c
arch/mips/kernel/perf_event.c
arch/mips/kernel/traps.c
arch/mips/kernel/unaligned.c
arch/mips/math-emu/cp1emu.c
arch/mips/mm/fault.c
arch/powerpc/include/asm/emulated_ops.h
arch/powerpc/kernel/perf_event.c
arch/powerpc/kernel/perf_event_fsl_emb.c
arch/powerpc/kernel/ptrace.c
arch/powerpc/mm/fault.c
arch/s390/mm/fault.c
arch/sh/kernel/ptrace_32.c
arch/sh/kernel/traps_32.c
arch/sh/kernel/traps_64.c
arch/sh/math-emu/math.c
arch/sh/mm/fault_32.c
arch/sh/mm/tlbflush_64.c
arch/sparc/kernel/perf_event.c
arch/sparc/kernel/unaligned_32.c
arch/sparc/kernel/unaligned_64.c
arch/sparc/kernel/visemul.c
arch/sparc/math-emu/math_32.c
arch/sparc/math-emu/math_64.c
arch/sparc/mm/fault_32.c
arch/sparc/mm/fault_64.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/cpu/perf_event_intel.c
arch/x86/kernel/cpu/perf_event_intel_ds.c
arch/x86/kernel/cpu/perf_event_p4.c
arch/x86/kernel/kgdb.c
arch/x86/kernel/ptrace.c
arch/x86/mm/fault.c
include/linux/perf_event.h
kernel/events/core.c
kernel/events/internal.h
kernel/events/ring_buffer.c
kernel/sched.c
kernel/watchdog.c
samples/hw_breakpoint/data_breakpoint.c
Commit message: perf: Remove the nmi parameter from the swevent and overflow interface

The nmi parameter indicated if we could do wakeups from the current
context, if not, we would set some state and self-IPI and let the
resulting interrupt do the wakeup.

For the various event classes:

  - hardware: nmi=0; PMI is in fact an NMI or we run irq_work_run from
    the PMI-tail (ARM etc.)
  - tracepoint: nmi=0; since tracepoint could be from NMI context.
  - software: nmi=[0,1]; some, like the schedule thing cannot
    perform wakeups, and hence need 0.

As one can see, there is very little nmi=1 usage, and the down-side of
not using it is that on some platforms some software events can have a
jiffy delay in wakeup (when arch_irq_work_raise isn't implemented).

The up-side however is that we can remove the nmi parameter and save a
bunch of conditionals in fast paths.

Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Michael Cree <mcree@orcon.net.nz>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Deng-Cheng Zhu <dengcheng.zhu@gmail.com>
Cc: Anton Blanchard <anton@samba.org>
Cc: Eric B Munson <emunson@mgebm.net>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jason Wessel <jason.wessel@windriver.com>
Cc: Don Zickus <dzickus@redhat.com>
Link: http://lkml.kernel.org/n/tip-agjev8eu666tvknpb3iaj0fg@git.kernel.org
Signed-off-by: Ingo Molnar <mingo@elte.hu>


Vulnerable code:
		if (perf_event_overflow(event, 1, &data, regs)) {
		if (perf_event_overflow(event, 0, &data, regs))
		if (perf_event_overflow(event, 0, &data, regs))
		if (perf_event_overflow(event, 0, &data, regs))
		if (perf_event_overflow(event, 0, &data, regs))
static void ptrace_hbptriggered(struct perf_event *bp, int unused,
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, regs->ARM_pc);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, addr);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, regs, addr);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, regs, addr);
	if (perf_event_overflow(event, 0, data, regs))
				1, 0, regs, 0);
				1, 0, regs, 0);
				1, 0, regs, 0);
				1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
		      1, 0, regs, 0);
			1, 0, regs, regs->cp0_badvaddr);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS,
			1, 0, xcp, 0);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ,
				1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN,
				1, 0, regs, address);
			1, 0, regs, 0);					\
			1, 0, regs, regs->dar);				\
			       struct pt_regs *regs, int nmi)
		if (perf_event_overflow(event, nmi, &data, regs))
			record_and_restart(event, val, regs, nmi);
			       struct pt_regs *regs, int nmi)
		if (perf_event_overflow(event, nmi, &data, regs))
				record_and_restart(event, val, regs, nmi);
void ptrace_triggered(struct perf_event *bp, int nmi,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
void ptrace_triggered(struct perf_event *bp, int nmi,
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0,
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, address);
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, address);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, address);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, address);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
		if (perf_event_overflow(event, 1, &data, regs))
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, addr);
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, addr);
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, addr);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, sfar);
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, 0, regs, sfar);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, 0, regs, 0);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
			      regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
			      regs, address);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
			      regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
			      regs, address);
		if (perf_event_overflow(event, 1, &data, regs))
		if (perf_event_overflow(event, 1, &data, regs))
	if (perf_output_begin(&handle, event, header.size * (top - at), 1, 1))
	if (perf_event_overflow(event, 1, &data, &regs))
		if (perf_event_overflow(event, 1, &data, regs))
static void kgdb_hw_overflow_handler(struct perf_event *event, int nmi,
static void ptrace_triggered(struct perf_event *bp, int nmi,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
typedef void (*perf_overflow_handler_t)(struct perf_event *, int,
	int				nmi;
extern int perf_event_overflow(struct perf_event *event, int nmi,
extern void __perf_sw_event(u32, u64, int, struct pt_regs *, u64);
perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr)
		__perf_sw_event(event_id, nr, nmi, regs, addr);
	perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, NULL, 0);
			     int nmi, int sample);
perf_sw_event(u32 event_id, u64 nr, int nmi,
		     struct pt_regs *regs, u64 addr)			{ }
static void perf_event_output(struct perf_event *event, int nmi,
	if (perf_output_begin(&handle, event, header.size, nmi, 1))
	ret = perf_output_begin(&handle, event, read_event.header.size, 0, 0);
				task_event->event_id.header.size, 0, 0);
				comm_event->event_id.header.size, 0, 0);
				mmap_event->event_id.header.size, 0, 0);
				throttle_event.header.size, 1, 0);
static int __perf_event_overflow(struct perf_event *event, int nmi,
		if (nmi) {
			event->pending_disable = 1;
			irq_work_queue(&event->pending);
		} else
			perf_event_disable(event);
		event->overflow_handler(event, nmi, data, regs);
		perf_event_output(event, nmi, data, regs);
		if (nmi) {
			event->pending_wakeup = 1;
			irq_work_queue(&event->pending);
		} else
			perf_event_wakeup(event);
int perf_event_overflow(struct perf_event *event, int nmi,
	return __perf_event_overflow(event, nmi, 1, data, regs);
				    int nmi, struct perf_sample_data *data,
		if (__perf_event_overflow(event, nmi, throttle,
			       int nmi, struct perf_sample_data *data,
		return perf_swevent_overflow(event, 1, nmi, data, regs);
	perf_swevent_overflow(event, 0, nmi, data, regs);
				    u64 nr, int nmi,
			perf_swevent_event(event, nr, nmi, data, regs);
void __perf_sw_event(u32 event_id, u64 nr, int nmi,
			    struct pt_regs *regs, u64 addr)
	do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, nmi, &data, regs);
			perf_swevent_event(event, count, 1, &data, regs);
		perf_swevent_event(bp, 1, 1, &sample, regs);
			if (perf_event_overflow(event, 0, &data, regs))

	if (handle->nmi) {
		handle->event->pending_wakeup = 1;
		irq_work_queue(&handle->event->pending);
	} else
		perf_event_wakeup(handle->event);
		      int nmi, int sample)
	handle->nmi	= nmi;
		perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, 1, NULL, 0);
static void watchdog_overflow_callback(struct perf_event *event, int nmi,
static void sample_hbp_handler(struct perf_event *bp, int nmi,

Fixed code:
		if (perf_event_overflow(event, &data, regs)) {
		if (perf_event_overflow(event, &data, regs))
		if (perf_event_overflow(event, &data, regs))
		if (perf_event_overflow(event, &data, regs))
		if (perf_event_overflow(event, &data, regs))
static void ptrace_hbptriggered(struct perf_event *bp,
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, addr);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, addr);
	if (perf_event_overflow(event, data, regs))
				1, regs, 0);
				1, regs, 0);
				1, regs, 0);
				1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
			1, regs, regs->cp0_badvaddr);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, xcp, 0);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address);
			1, regs, 0);					\
			1, regs, regs->dar);				\
			       struct pt_regs *regs)
		if (perf_event_overflow(event, &data, regs))
			record_and_restart(event, val, regs);
			       struct pt_regs *regs)
		if (perf_event_overflow(event, &data, regs))
				record_and_restart(event, val, regs);
void ptrace_triggered(struct perf_event *bp,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
void ptrace_triggered(struct perf_event *bp,
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1,
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address);
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, address);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
		if (perf_event_overflow(event, &data, regs))
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, addr);
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, addr);
		perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, addr);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, sfar);
	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, sfar);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address);
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs, address);
		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address);
		if (perf_event_overflow(event, &data, regs))
		if (perf_event_overflow(event, &data, regs))
	if (perf_output_begin(&handle, event, header.size * (top - at), 1))
	if (perf_event_overflow(event, &data, &regs))
		if (perf_event_overflow(event, &data, regs))
static void kgdb_hw_overflow_handler(struct perf_event *event,
static void ptrace_triggered(struct perf_event *bp,
	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
typedef void (*perf_overflow_handler_t)(struct perf_event *,
extern int perf_event_overflow(struct perf_event *event,
extern void __perf_sw_event(u32, u64, struct pt_regs *, u64);
perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
		__perf_sw_event(event_id, nr, regs, addr);
	perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0);
			     int sample);
perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)	{ }
static void perf_event_output(struct perf_event *event,
	if (perf_output_begin(&handle, event, header.size, 1))
	ret = perf_output_begin(&handle, event, read_event.header.size, 0);
				task_event->event_id.header.size, 0);
				comm_event->event_id.header.size, 0);
				mmap_event->event_id.header.size, 0);
				throttle_event.header.size, 0);
static int __perf_event_overflow(struct perf_event *event,
		event->pending_disable = 1;
		irq_work_queue(&event->pending);
		event->overflow_handler(event, data, regs);
		perf_event_output(event, data, regs);
		event->pending_wakeup = 1;
		irq_work_queue(&event->pending);
int perf_event_overflow(struct perf_event *event,
	return __perf_event_overflow(event, 1, data, regs);
				    struct perf_sample_data *data,
		if (__perf_event_overflow(event, throttle,
			       struct perf_sample_data *data,
		return perf_swevent_overflow(event, 1, data, regs);
	perf_swevent_overflow(event, 0, data, regs);
				    u64 nr,
			perf_swevent_event(event, nr, data, regs);
void __perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
	do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, &data, regs);
			perf_swevent_event(event, count, &data, regs);
		perf_swevent_event(bp, 1, &sample, regs);
			if (perf_event_overflow(event, &data, regs))
	handle->event->pending_wakeup = 1;
	irq_work_queue(&handle->event->pending);
		      int sample)
		perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, NULL, 0);
static void watchdog_overflow_callback(struct perf_event *event,
static void sample_hbp_handler(struct perf_event *bp,