diff options
| -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 | } |
