diff options
Diffstat (limited to 'arch/x86/kernel/stacktrace.c')
-rw-r--r-- | arch/x86/kernel/stacktrace.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index a03e7f6d90c3..b15153060417 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
7 | #include <linux/stacktrace.h> | 7 | #include <linux/stacktrace.h> |
8 | #include <linux/module.h> | 8 | #include <linux/module.h> |
9 | #include <linux/uaccess.h> | ||
9 | #include <asm/stacktrace.h> | 10 | #include <asm/stacktrace.h> |
10 | 11 | ||
11 | static void save_stack_warning(void *data, char *msg) | 12 | static void save_stack_warning(void *data, char *msg) |
@@ -83,3 +84,59 @@ void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | |||
83 | trace->entries[trace->nr_entries++] = ULONG_MAX; | 84 | trace->entries[trace->nr_entries++] = ULONG_MAX; |
84 | } | 85 | } |
85 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | 86 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); |
87 | |||
88 | /* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */ | ||
89 | |||
90 | struct stack_frame { | ||
91 | const void __user *next_fp; | ||
92 | unsigned long return_address; | ||
93 | }; | ||
94 | |||
95 | static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) | ||
96 | { | ||
97 | int ret; | ||
98 | |||
99 | if (!access_ok(VERIFY_READ, fp, sizeof(*frame))) | ||
100 | return 0; | ||
101 | |||
102 | ret = 1; | ||
103 | pagefault_disable(); | ||
104 | if (__copy_from_user_inatomic(frame, fp, sizeof(*frame))) | ||
105 | ret = 0; | ||
106 | pagefault_enable(); | ||
107 | |||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | void save_stack_trace_user(struct stack_trace *trace) | ||
112 | { | ||
113 | /* | ||
114 | * Trace user stack if we are not a kernel thread | ||
115 | */ | ||
116 | if (current->mm) { | ||
117 | const struct pt_regs *regs = task_pt_regs(current); | ||
118 | const void __user *fp = (const void __user *)regs->bp; | ||
119 | |||
120 | if (trace->nr_entries < trace->max_entries) | ||
121 | trace->entries[trace->nr_entries++] = regs->ip; | ||
122 | |||
123 | while (trace->nr_entries < trace->max_entries) { | ||
124 | struct stack_frame frame; | ||
125 | frame.next_fp = NULL; | ||
126 | frame.return_address = 0; | ||
127 | if (!copy_stack_frame(fp, &frame)) | ||
128 | break; | ||
129 | if ((unsigned long)fp < regs->sp) | ||
130 | break; | ||
131 | if (frame.return_address) | ||
132 | trace->entries[trace->nr_entries++] = | ||
133 | frame.return_address; | ||
134 | if (fp == frame.next_fp) | ||
135 | break; | ||
136 | fp = frame.next_fp; | ||
137 | } | ||
138 | } | ||
139 | if (trace->nr_entries < trace->max_entries) | ||
140 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
141 | } | ||
142 | |||