| From 1b15ec7a7427d4188ba91b9bbac696250a059d22 Mon Sep 17 00:00:00 2001 |
| From: Konstantin Khlebnikov <k.khlebnikov@samsung.com> |
| Date: Thu, 5 Dec 2013 14:21:36 +0100 |
| Subject: ARM: 7912/1: check stack pointer in get_wchan |
| |
| From: Konstantin Khlebnikov <k.khlebnikov@samsung.com> |
| |
| commit 1b15ec7a7427d4188ba91b9bbac696250a059d22 upstream. |
| |
| get_wchan() is lockless. Task may wakeup at any time and change its own stack, |
| thus each next stack frame may be overwritten and filled with random stuff. |
| |
| /proc/$pid/stack interface had been disabled for non-current tasks, see [1] |
| But 'wchan' still allows to trigger stack frame unwinding on volatile stack. |
| |
| This patch fixes oops in unwind_frame() by adding stack pointer validation on |
| each step (as x86 code do), unwind_frame() already checks frame pointer. |
| |
| Also I've found another report of this oops on stackoverflow (irony). |
| |
| Link: http://www.spinics.net/lists/arm-kernel/msg110589.html [1] |
| Link: http://stackoverflow.com/questions/18479894/unwind-frame-cause-a-kernel-paging-error |
| |
| Signed-off-by: Konstantin Khlebnikov <k.khlebnikov@samsung.com> |
| Acked-by: Will Deacon <will.deacon@arm.com> |
| Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/arm/kernel/process.c | 7 +++++-- |
| 1 file changed, 5 insertions(+), 2 deletions(-) |
| |
| --- a/arch/arm/kernel/process.c |
| +++ b/arch/arm/kernel/process.c |
| @@ -404,6 +404,7 @@ EXPORT_SYMBOL(dump_fpu); |
| unsigned long get_wchan(struct task_struct *p) |
| { |
| struct stackframe frame; |
| + unsigned long stack_page; |
| int count = 0; |
| if (!p || p == current || p->state == TASK_RUNNING) |
| return 0; |
| @@ -412,9 +413,11 @@ unsigned long get_wchan(struct task_stru |
| frame.sp = thread_saved_sp(p); |
| frame.lr = 0; /* recovered from the stack */ |
| frame.pc = thread_saved_pc(p); |
| + stack_page = (unsigned long)task_stack_page(p); |
| do { |
| - int ret = unwind_frame(&frame); |
| - if (ret < 0) |
| + if (frame.sp < stack_page || |
| + frame.sp >= stack_page + THREAD_SIZE || |
| + unwind_frame(&frame) < 0) |
| return 0; |
| if (!in_sched_functions(frame.pc)) |
| return frame.pc; |