diff options
-rw-r--r-- | arch/x86/oprofile/backtrace.c | 56 |
1 files changed, 47 insertions, 9 deletions
diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index a5b64ab4cd6e..32f78eb46744 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c | |||
@@ -11,10 +11,12 @@ | |||
11 | #include <linux/oprofile.h> | 11 | #include <linux/oprofile.h> |
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
14 | #include <linux/compat.h> | ||
15 | #include <linux/highmem.h> | ||
16 | |||
14 | #include <asm/ptrace.h> | 17 | #include <asm/ptrace.h> |
15 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
16 | #include <asm/stacktrace.h> | 19 | #include <asm/stacktrace.h> |
17 | #include <linux/compat.h> | ||
18 | 20 | ||
19 | static int backtrace_stack(void *data, char *name) | 21 | static int backtrace_stack(void *data, char *name) |
20 | { | 22 | { |
@@ -36,17 +38,53 @@ static struct stacktrace_ops backtrace_ops = { | |||
36 | .walk_stack = print_context_stack, | 38 | .walk_stack = print_context_stack, |
37 | }; | 39 | }; |
38 | 40 | ||
41 | /* from arch/x86/kernel/cpu/perf_event.c: */ | ||
42 | |||
43 | /* | ||
44 | * best effort, GUP based copy_from_user() that assumes IRQ or NMI context | ||
45 | */ | ||
46 | static unsigned long | ||
47 | copy_from_user_nmi(void *to, const void __user *from, unsigned long n) | ||
48 | { | ||
49 | unsigned long offset, addr = (unsigned long)from; | ||
50 | unsigned long size, len = 0; | ||
51 | struct page *page; | ||
52 | void *map; | ||
53 | int ret; | ||
54 | |||
55 | do { | ||
56 | ret = __get_user_pages_fast(addr, 1, 0, &page); | ||
57 | if (!ret) | ||
58 | break; | ||
59 | |||
60 | offset = addr & (PAGE_SIZE - 1); | ||
61 | size = min(PAGE_SIZE - offset, n - len); | ||
62 | |||
63 | map = kmap_atomic(page); | ||
64 | memcpy(to, map+offset, size); | ||
65 | kunmap_atomic(map); | ||
66 | put_page(page); | ||
67 | |||
68 | len += size; | ||
69 | to += size; | ||
70 | addr += size; | ||
71 | |||
72 | } while (len < n); | ||
73 | |||
74 | return len; | ||
75 | } | ||
76 | |||
39 | #ifdef CONFIG_COMPAT | 77 | #ifdef CONFIG_COMPAT |
40 | static struct stack_frame_ia32 * | 78 | static struct stack_frame_ia32 * |
41 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | 79 | dump_user_backtrace_32(struct stack_frame_ia32 *head) |
42 | { | 80 | { |
81 | /* Also check accessibility of one struct frame_head beyond: */ | ||
43 | struct stack_frame_ia32 bufhead[2]; | 82 | struct stack_frame_ia32 bufhead[2]; |
44 | struct stack_frame_ia32 *fp; | 83 | struct stack_frame_ia32 *fp; |
84 | unsigned long bytes; | ||
45 | 85 | ||
46 | /* Also check accessibility of one struct frame_head beyond */ | 86 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
47 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 87 | if (bytes != sizeof(bufhead)) |
48 | return NULL; | ||
49 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
50 | return NULL; | 88 | return NULL; |
51 | 89 | ||
52 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); | 90 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); |
@@ -87,12 +125,12 @@ x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | |||
87 | 125 | ||
88 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | 126 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) |
89 | { | 127 | { |
128 | /* Also check accessibility of one struct frame_head beyond: */ | ||
90 | struct stack_frame bufhead[2]; | 129 | struct stack_frame bufhead[2]; |
130 | unsigned long bytes; | ||
91 | 131 | ||
92 | /* Also check accessibility of one struct stack_frame beyond */ | 132 | bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); |
93 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | 133 | if (bytes != sizeof(bufhead)) |
94 | return NULL; | ||
95 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
96 | return NULL; | 134 | return NULL; |
97 | 135 | ||
98 | oprofile_add_trace(bufhead[0].return_address); | 136 | oprofile_add_trace(bufhead[0].return_address); |