1 From fc18822510721fe694d273c5211c71ea52796d76 Mon Sep 17 00:00:00 2001
2 From: Josh Poimboeuf <jpoimboe@redhat.com>
3 Date: Fri, 1 Jul 2016 23:02:05 -0500
4 Subject: perf/x86: Fix 32-bit perf user callgraph collection
6 From: Josh Poimboeuf <jpoimboe@redhat.com>
8 commit fc18822510721fe694d273c5211c71ea52796d76 upstream.
10 A basic perf callgraph record operation causes an immediate panic on a
11 32-bit kernel compiled with CONFIG_CC_STACKPROTECTOR=y:
14 Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: c0404fbd
16 CPU: 0 PID: 998 Comm: ls Not tainted 4.7.0-rc5+ #1
17 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.9.1-1.fc24 04/01/2014
18 c0dd5967 ff7afe1c 00000086 f41dbc2c c07445a0 464c457f f41dbca8 f41dbc44
19 c05646f4 f41dbca8 464c457f f41dbca8 464c457f f41dbc54 c04625be c0ce56fc
20 c0404fbd f41dbc88 c0404fbd b74668f0 f41dc000 00000000 c0000000 00000000
22 [<c07445a0>] dump_stack+0x58/0x78
23 [<c05646f4>] panic+0x8e/0x1c6
24 [<c04625be>] __stack_chk_fail+0x1e/0x30
25 [<c0404fbd>] ? perf_callchain_user+0x22d/0x230
26 [<c0404fbd>] perf_callchain_user+0x22d/0x230
27 [<c055f89f>] get_perf_callchain+0x1ff/0x270
28 [<c055f988>] perf_callchain+0x78/0x90
29 [<c055c7eb>] perf_prepare_sample+0x24b/0x370
30 [<c055c934>] perf_event_output_forward+0x24/0x70
31 [<c05531c0>] __perf_event_overflow+0xa0/0x210
32 [<c0550a93>] ? cpu_clock_event_read+0x43/0x50
33 [<c0553431>] perf_swevent_hrtimer+0x101/0x180
34 [<c0456235>] ? kmap_atomic_prot+0x35/0x140
35 [<c056dc69>] ? get_page_from_freelist+0x279/0x950
36 [<c058fdd8>] ? vma_interval_tree_remove+0x158/0x230
37 [<c05939f4>] ? wp_page_copy.isra.82+0x2f4/0x630
38 [<c05a050d>] ? page_add_file_rmap+0x1d/0x50
39 [<c0565611>] ? unlock_page+0x61/0x80
40 [<c0566755>] ? filemap_map_pages+0x305/0x320
41 [<c059769f>] ? handle_mm_fault+0xb7f/0x1560
42 [<c074cbeb>] ? timerqueue_del+0x1b/0x70
43 [<c04cfefe>] ? __remove_hrtimer+0x2e/0x60
44 [<c04d017b>] __hrtimer_run_queues+0xcb/0x2a0
45 [<c0553330>] ? __perf_event_overflow+0x210/0x210
46 [<c04d0a2a>] hrtimer_interrupt+0x8a/0x180
47 [<c043ecc2>] local_apic_timer_interrupt+0x32/0x60
48 [<c043f643>] smp_apic_timer_interrupt+0x33/0x50
49 [<c0b0cd38>] apic_timer_interrupt+0x34/0x3c
50 Kernel Offset: disabled
51 ---[ end Kernel panic - not syncing: stack-protector: Kernel stack is corrupted in: c0404fbd
53 The panic is caused by the fact that perf_callchain_user() mistakenly
54 assumes it's 64-bit only and ends up corrupting the stack.
56 Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
57 Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
58 Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
59 Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
60 Cc: Jiri Olsa <jolsa@redhat.com>
61 Cc: Linus Torvalds <torvalds@linux-foundation.org>
62 Cc: Peter Zijlstra <peterz@infradead.org>
63 Cc: Stephane Eranian <eranian@google.com>
64 Cc: Thomas Gleixner <tglx@linutronix.de>
65 Cc: Vince Weaver <vincent.weaver@maine.edu>
66 Fixes: 75925e1ad7f5 ("perf/x86: Optimize stack walk user accesses")
67 Link: http://lkml.kernel.org/r/1a547f5077ec30f75f9b57074837c3c80df86e5e.1467432113.git.jpoimboe@redhat.com
68 Signed-off-by: Ingo Molnar <mingo@kernel.org>
69 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
72 arch/x86/events/core.c | 11 ++++++-----
73 1 file changed, 6 insertions(+), 5 deletions(-)
75 --- a/arch/x86/events/core.c
76 +++ b/arch/x86/events/core.c
77 @@ -2313,7 +2313,7 @@ void
78 perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs)
80 struct stack_frame frame;
81 - const void __user *fp;
82 + const unsigned long __user *fp;
84 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
85 /* TODO: We don't support guest os callchain now */
86 @@ -2326,7 +2326,7 @@ perf_callchain_user(struct perf_callchai
87 if (regs->flags & (X86_VM_MASK | PERF_EFLAGS_VM))
90 - fp = (void __user *)regs->bp;
91 + fp = (unsigned long __user *)regs->bp;
93 perf_callchain_store(entry, regs->ip);
95 @@ -2339,16 +2339,17 @@ perf_callchain_user(struct perf_callchai
97 while (entry->nr < PERF_MAX_STACK_DEPTH) {
100 frame.next_frame = NULL;
101 frame.return_address = 0;
103 - if (!access_ok(VERIFY_READ, fp, 16))
104 + if (!access_ok(VERIFY_READ, fp, sizeof(*fp) * 2))
107 - bytes = __copy_from_user_nmi(&frame.next_frame, fp, 8);
108 + bytes = __copy_from_user_nmi(&frame.next_frame, fp, sizeof(*fp));
111 - bytes = __copy_from_user_nmi(&frame.return_address, fp+8, 8);
112 + bytes = __copy_from_user_nmi(&frame.return_address, fp + 1, sizeof(*fp));