diff options
Diffstat (limited to 'arch/x86/kernel/unwind_frame.c')
-rw-r--r-- | arch/x86/kernel/unwind_frame.c | 38 |
1 files changed, 36 insertions, 2 deletions
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c index d145a0b1f529..3dc26f95d46e 100644 --- a/arch/x86/kernel/unwind_frame.c +++ b/arch/x86/kernel/unwind_frame.c | |||
@@ -44,7 +44,8 @@ static void unwind_dump(struct unwind_state *state) | |||
44 | state->stack_info.type, state->stack_info.next_sp, | 44 | state->stack_info.type, state->stack_info.next_sp, |
45 | state->stack_mask, state->graph_idx); | 45 | state->stack_mask, state->graph_idx); |
46 | 46 | ||
47 | for (sp = state->orig_sp; sp; sp = PTR_ALIGN(stack_info.next_sp, sizeof(long))) { | 47 | for (sp = PTR_ALIGN(state->orig_sp, sizeof(long)); sp; |
48 | sp = PTR_ALIGN(stack_info.next_sp, sizeof(long))) { | ||
48 | if (get_stack_info(sp, state->task, &stack_info, &visit_mask)) | 49 | if (get_stack_info(sp, state->task, &stack_info, &visit_mask)) |
49 | break; | 50 | break; |
50 | 51 | ||
@@ -174,6 +175,7 @@ static bool is_last_task_frame(struct unwind_state *state) | |||
174 | * This determines if the frame pointer actually contains an encoded pointer to | 175 | * This determines if the frame pointer actually contains an encoded pointer to |
175 | * pt_regs on the stack. See ENCODE_FRAME_POINTER. | 176 | * pt_regs on the stack. See ENCODE_FRAME_POINTER. |
176 | */ | 177 | */ |
178 | #ifdef CONFIG_X86_64 | ||
177 | static struct pt_regs *decode_frame_pointer(unsigned long *bp) | 179 | static struct pt_regs *decode_frame_pointer(unsigned long *bp) |
178 | { | 180 | { |
179 | unsigned long regs = (unsigned long)bp; | 181 | unsigned long regs = (unsigned long)bp; |
@@ -183,6 +185,23 @@ static struct pt_regs *decode_frame_pointer(unsigned long *bp) | |||
183 | 185 | ||
184 | return (struct pt_regs *)(regs & ~0x1); | 186 | return (struct pt_regs *)(regs & ~0x1); |
185 | } | 187 | } |
188 | #else | ||
189 | static struct pt_regs *decode_frame_pointer(unsigned long *bp) | ||
190 | { | ||
191 | unsigned long regs = (unsigned long)bp; | ||
192 | |||
193 | if (regs & 0x80000000) | ||
194 | return NULL; | ||
195 | |||
196 | return (struct pt_regs *)(regs | 0x80000000); | ||
197 | } | ||
198 | #endif | ||
199 | |||
200 | #ifdef CONFIG_X86_32 | ||
201 | #define KERNEL_REGS_SIZE (sizeof(struct pt_regs) - 2*sizeof(long)) | ||
202 | #else | ||
203 | #define KERNEL_REGS_SIZE (sizeof(struct pt_regs)) | ||
204 | #endif | ||
186 | 205 | ||
187 | static bool update_stack_state(struct unwind_state *state, | 206 | static bool update_stack_state(struct unwind_state *state, |
188 | unsigned long *next_bp) | 207 | unsigned long *next_bp) |
@@ -202,7 +221,7 @@ static bool update_stack_state(struct unwind_state *state, | |||
202 | regs = decode_frame_pointer(next_bp); | 221 | regs = decode_frame_pointer(next_bp); |
203 | if (regs) { | 222 | if (regs) { |
204 | frame = (unsigned long *)regs; | 223 | frame = (unsigned long *)regs; |
205 | len = regs_size(regs); | 224 | len = KERNEL_REGS_SIZE; |
206 | state->got_irq = true; | 225 | state->got_irq = true; |
207 | } else { | 226 | } else { |
208 | frame = next_bp; | 227 | frame = next_bp; |
@@ -226,6 +245,14 @@ static bool update_stack_state(struct unwind_state *state, | |||
226 | frame < prev_frame_end) | 245 | frame < prev_frame_end) |
227 | return false; | 246 | return false; |
228 | 247 | ||
248 | /* | ||
249 | * On 32-bit with user mode regs, make sure the last two regs are safe | ||
250 | * to access: | ||
251 | */ | ||
252 | if (IS_ENABLED(CONFIG_X86_32) && regs && user_mode(regs) && | ||
253 | !on_stack(info, frame, len + 2*sizeof(long))) | ||
254 | return false; | ||
255 | |||
229 | /* Move state to the next frame: */ | 256 | /* Move state to the next frame: */ |
230 | if (regs) { | 257 | if (regs) { |
231 | state->regs = regs; | 258 | state->regs = regs; |
@@ -328,6 +355,13 @@ bad_address: | |||
328 | state->regs->sp < (unsigned long)task_pt_regs(state->task)) | 355 | state->regs->sp < (unsigned long)task_pt_regs(state->task)) |
329 | goto the_end; | 356 | goto the_end; |
330 | 357 | ||
358 | /* | ||
359 | * There are some known frame pointer issues on 32-bit. Disable | ||
360 | * unwinder warnings on 32-bit until it gets objtool support. | ||
361 | */ | ||
362 | if (IS_ENABLED(CONFIG_X86_32)) | ||
363 | goto the_end; | ||
364 | |||
331 | if (state->regs) { | 365 | if (state->regs) { |
332 | printk_deferred_once(KERN_WARNING | 366 | printk_deferred_once(KERN_WARNING |
333 | "WARNING: kernel stack regs at %p in %s:%d has bad 'bp' value %p\n", | 367 | "WARNING: kernel stack regs at %p in %s:%d has bad 'bp' value %p\n", |