diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/oprofile/backtrace.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index d640a86198b1..2d49d4e19a36 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <asm/ptrace.h> | 14 | #include <asm/ptrace.h> |
15 | #include <asm/uaccess.h> | 15 | #include <asm/uaccess.h> |
16 | #include <asm/stacktrace.h> | 16 | #include <asm/stacktrace.h> |
17 | #include <linux/compat.h> | ||
17 | 18 | ||
18 | static void backtrace_warning_symbol(void *data, char *msg, | 19 | static void backtrace_warning_symbol(void *data, char *msg, |
19 | unsigned long symbol) | 20 | unsigned long symbol) |
@@ -48,6 +49,55 @@ static struct stacktrace_ops backtrace_ops = { | |||
48 | .walk_stack = print_context_stack, | 49 | .walk_stack = print_context_stack, |
49 | }; | 50 | }; |
50 | 51 | ||
52 | #ifdef CONFIG_COMPAT | ||
53 | static struct stack_frame_ia32 * | ||
54 | dump_user_backtrace_32(struct stack_frame_ia32 *head) | ||
55 | { | ||
56 | struct stack_frame_ia32 bufhead[2]; | ||
57 | struct stack_frame_ia32 *fp; | ||
58 | |||
59 | /* Also check accessibility of one struct frame_head beyond */ | ||
60 | if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) | ||
61 | return NULL; | ||
62 | if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) | ||
63 | return NULL; | ||
64 | |||
65 | fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); | ||
66 | |||
67 | oprofile_add_trace(bufhead[0].return_address); | ||
68 | |||
69 | /* frame pointers should strictly progress back up the stack | ||
70 | * (towards higher addresses) */ | ||
71 | if (head >= fp) | ||
72 | return NULL; | ||
73 | |||
74 | return fp; | ||
75 | } | ||
76 | |||
77 | static inline int | ||
78 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | ||
79 | { | ||
80 | struct stack_frame_ia32 *head; | ||
81 | |||
82 | /* User process is 32-bit */ | ||
83 | if (!current || !test_thread_flag(TIF_IA32)) | ||
84 | return 0; | ||
85 | |||
86 | head = (struct stack_frame_ia32 *) regs->bp; | ||
87 | while (depth-- && head) | ||
88 | head = dump_user_backtrace_32(head); | ||
89 | |||
90 | return 1; | ||
91 | } | ||
92 | |||
93 | #else | ||
94 | static inline int | ||
95 | x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) | ||
96 | { | ||
97 | return 0; | ||
98 | } | ||
99 | #endif /* CONFIG_COMPAT */ | ||
100 | |||
51 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) | 101 | static struct stack_frame *dump_user_backtrace(struct stack_frame *head) |
52 | { | 102 | { |
53 | struct stack_frame bufhead[2]; | 103 | struct stack_frame bufhead[2]; |
@@ -81,6 +131,9 @@ x86_backtrace(struct pt_regs * const regs, unsigned int depth) | |||
81 | return; | 131 | return; |
82 | } | 132 | } |
83 | 133 | ||
134 | if (x86_backtrace_32(regs, depth)) | ||
135 | return; | ||
136 | |||
84 | while (depth-- && head) | 137 | while (depth-- && head) |
85 | head = dump_user_backtrace(head); | 138 | head = dump_user_backtrace(head); |
86 | } | 139 | } |