diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index cd426c3283ee1..f2f733bcb2b95 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -945,13 +945,58 @@ unsigned long arch_randomize_brk(struct mm_struct *mm) */ unsigned long get_wchan(struct task_struct *p) { - unsigned long entry = 0; + unsigned long start, bottom, top, sp, fp, ip, ret = 0; + int count = 0; if (p == current || task_is_running(p)) return 0; - stack_trace_save_tsk(p, &entry, 1, 0); - return entry; + if (!try_get_task_stack(p)) + return 0; + + start = (unsigned long)task_stack_page(p); + if (!start) + goto out; + + /* + * Layout of the stack page: + * + * ----------- topmax = start + THREAD_SIZE - sizeof(unsigned long) + * PADDING + * ----------- top = topmax - TOP_OF_KERNEL_STACK_PADDING + * stack + * ----------- bottom = start + * + * The tasks stack pointer points at the location where the + * framepointer is stored. The data on the stack is: + * ... IP FP ... IP FP + * + * We need to read FP and IP, so we need to adjust the upper + * bound by another unsigned long. + */ + top = start + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING; + top -= 2 * sizeof(unsigned long); + bottom = start; + + sp = READ_ONCE(p->thread.sp); + if (sp < bottom || sp > top) + goto out; + + fp = READ_ONCE_NOCHECK(((struct inactive_task_frame *)sp)->bp); + do { + if (fp < bottom || fp > top) + goto out; + ip = READ_ONCE_NOCHECK(*(unsigned long *)(fp + sizeof(unsigned long))); + if (!in_sched_functions(ip)) { + ret = ip; + goto out; + } + fp = READ_ONCE_NOCHECK(*(unsigned long *)fp); + } while (count++ < 16 && !task_is_running(p)); + +out: + put_task_stack(p); + return ret; } long do_arch_prctl_common(struct task_struct *task, int option,