aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Poimboeuf <jpoimboe@redhat.com>2017-01-09 13:00:23 -0500
committerIngo Molnar <mingo@kernel.org>2017-01-12 03:28:27 -0500
commit84936118bdf37bda513d4a361c38181a216427e0 (patch)
tree811f0e8e4c6776d12e7315c076a12a32c3ecf093
parent900742d89c1b4e04bd373aec8470b88e183f08ca (diff)
x86/unwind: Disable KASAN checks for non-current tasks
There are a handful of callers to save_stack_trace_tsk() and show_stack() which try to unwind the stack of a task other than current. In such cases, it's remotely possible that the task is running on one CPU while the unwinder is reading its stack from another CPU, causing the unwinder to see stack corruption. These cases seem to be mostly harmless. The unwinder has checks which prevent it from following bad pointers beyond the bounds of the stack. So it's not really a bug as long as the caller understands that unwinding another task will not always succeed. In such cases, it's possible that the unwinder may read a KASAN-poisoned region of the stack. Account for that by using READ_ONCE_NOCHECK() when reading the stack of another task. Use READ_ONCE() when reading the stack of the current task, since KASAN warnings can still be useful for finding bugs in that case. Reported-by: Dmitry Vyukov <dvyukov@google.com> Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Dave Jones <davej@codemonkey.org.uk> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Miroslav Benes <mbenes@suse.cz> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/4c575eb288ba9f73d498dfe0acde2f58674598f1.1483978430.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/stacktrace.h5
-rw-r--r--arch/x86/kernel/unwind_frame.c20
2 files changed, 22 insertions, 3 deletions
diff --git a/arch/x86/include/asm/stacktrace.h b/arch/x86/include/asm/stacktrace.h
index a3269c897ec5..20ce3db20f24 100644
--- a/arch/x86/include/asm/stacktrace.h
+++ b/arch/x86/include/asm/stacktrace.h
@@ -52,13 +52,16 @@ static inline bool on_stack(struct stack_info *info, void *addr, size_t len)
52static inline unsigned long * 52static inline unsigned long *
53get_frame_pointer(struct task_struct *task, struct pt_regs *regs) 53get_frame_pointer(struct task_struct *task, struct pt_regs *regs)
54{ 54{
55 struct inactive_task_frame *frame;
56
55 if (regs) 57 if (regs)
56 return (unsigned long *)regs->bp; 58 return (unsigned long *)regs->bp;
57 59
58 if (task == current) 60 if (task == current)
59 return __builtin_frame_address(0); 61 return __builtin_frame_address(0);
60 62
61 return (unsigned long *)((struct inactive_task_frame *)task->thread.sp)->bp; 63 frame = (struct inactive_task_frame *)task->thread.sp;
64 return (unsigned long *)READ_ONCE_NOCHECK(frame->bp);
62} 65}
63#else 66#else
64static inline unsigned long * 67static inline unsigned long *
diff --git a/arch/x86/kernel/unwind_frame.c b/arch/x86/kernel/unwind_frame.c
index 195eebf6da20..23d15565d02a 100644
--- a/arch/x86/kernel/unwind_frame.c
+++ b/arch/x86/kernel/unwind_frame.c
@@ -6,6 +6,21 @@
6 6
7#define FRAME_HEADER_SIZE (sizeof(long) * 2) 7#define FRAME_HEADER_SIZE (sizeof(long) * 2)
8 8
9/*
10 * This disables KASAN checking when reading a value from another task's stack,
11 * since the other task could be running on another CPU and could have poisoned
12 * the stack in the meantime.
13 */
14#define READ_ONCE_TASK_STACK(task, x) \
15({ \
16 unsigned long val; \
17 if (task == current) \
18 val = READ_ONCE(x); \
19 else \
20 val = READ_ONCE_NOCHECK(x); \
21 val; \
22})
23
9static void unwind_dump(struct unwind_state *state, unsigned long *sp) 24static void unwind_dump(struct unwind_state *state, unsigned long *sp)
10{ 25{
11 static bool dumped_before = false; 26 static bool dumped_before = false;
@@ -48,7 +63,8 @@ unsigned long unwind_get_return_address(struct unwind_state *state)
48 if (state->regs && user_mode(state->regs)) 63 if (state->regs && user_mode(state->regs))
49 return 0; 64 return 0;
50 65
51 addr = ftrace_graph_ret_addr(state->task, &state->graph_idx, *addr_p, 66 addr = READ_ONCE_TASK_STACK(state->task, *addr_p);
67 addr = ftrace_graph_ret_addr(state->task, &state->graph_idx, addr,
52 addr_p); 68 addr_p);
53 69
54 return __kernel_text_address(addr) ? addr : 0; 70 return __kernel_text_address(addr) ? addr : 0;
@@ -162,7 +178,7 @@ bool unwind_next_frame(struct unwind_state *state)
162 if (state->regs) 178 if (state->regs)
163 next_bp = (unsigned long *)state->regs->bp; 179 next_bp = (unsigned long *)state->regs->bp;
164 else 180 else
165 next_bp = (unsigned long *)*state->bp; 181 next_bp = (unsigned long *)READ_ONCE_TASK_STACK(state->task,*state->bp);
166 182
167 /* is the next frame pointer an encoded pointer to pt_regs? */ 183 /* is the next frame pointer an encoded pointer to pt_regs? */
168 regs = decode_frame_pointer(next_bp); 184 regs = decode_frame_pointer(next_bp);