x86: Fix get_wchan() to support the ORC unwinder
authorQi Zheng <zhengqi.arch@bytedance.com>
Wed, 29 Sep 2021 22:02:17 +0000 (15:02 -0700)
committerPeter Zijlstra <peterz@infradead.org>
Fri, 15 Oct 2021 09:25:14 +0000 (11:25 +0200)
Currently, the kernel CONFIG_UNWINDER_ORC option is enabled by default
on x86, but the implementation of get_wchan() is still based on the frame
pointer unwinder, so the /proc/<pid>/wchan usually returned 0 regardless
of whether the task <pid> is running.

Reimplement get_wchan() by calling stack_trace_save_tsk(), which is
adapted to the ORC and frame pointer unwinders.

Fixes: ee9f8fce9964 ("x86/unwind: Add the ORC unwinder")
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20211008111626.271115116@infradead.org
arch/x86/kernel/process.c

index 1d9463e3096b68307e96445026e19cd01d98464e..e645925f9f027bda7a1f58b2dfdf6f59a92f922b 100644 (file)
@@ -944,58 +944,13 @@ unsigned long arch_randomize_brk(struct mm_struct *mm)
  */
 unsigned long get_wchan(struct task_struct *p)
 {
-       unsigned long start, bottom, top, sp, fp, ip, ret = 0;
-       int count = 0;
+       unsigned long entry = 0;
 
        if (p == current || task_is_running(p))
                return 0;
 
-       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;
+       stack_trace_save_tsk(p, &entry, 1, 0);
+       return entry;
 }
 
 long do_arch_prctl_common(struct task_struct *task, int option,