In preparation for being able to determine whether a given stack trace
is reliable, allow the stacktrace_ops functions to propagate errors to
dump_trace().

Signed-off-by: Josh Poimboeuf <jpoim...@redhat.com>
---
 arch/x86/include/asm/stacktrace.h | 36 +++++++++++++++-----------
 arch/x86/kernel/dumpstack.c       | 31 +++++++++++------------
 arch/x86/kernel/dumpstack_32.c    | 22 ++++++++++------
 arch/x86/kernel/dumpstack_64.c    | 53 ++++++++++++++++++++++++++-------------
 4 files changed, 87 insertions(+), 55 deletions(-)

diff --git a/arch/x86/include/asm/stacktrace.h 
b/arch/x86/include/asm/stacktrace.h
index 7c247e7..a64523f3 100644
--- a/arch/x86/include/asm/stacktrace.h
+++ b/arch/x86/include/asm/stacktrace.h
@@ -14,26 +14,32 @@ extern int kstack_depth_to_print;
 struct thread_info;
 struct stacktrace_ops;
 
-typedef unsigned long (*walk_stack_t)(struct thread_info *tinfo,
-                                     unsigned long *stack,
-                                     unsigned long bp,
-                                     const struct stacktrace_ops *ops,
-                                     void *data,
-                                     unsigned long *end,
-                                     int *graph);
-
-extern unsigned long
+typedef int (*walk_stack_t)(struct thread_info *tinfo,
+                           unsigned long *stack,
+                           unsigned long *bp,
+                           const struct stacktrace_ops *ops,
+                           void *data,
+                           unsigned long *end,
+                           int *graph);
+
+extern int
 print_context_stack(struct thread_info *tinfo,
-                   unsigned long *stack, unsigned long bp,
+                   unsigned long *stack, unsigned long *bp,
                    const struct stacktrace_ops *ops, void *data,
                    unsigned long *end, int *graph);
 
-extern unsigned long
+extern int
 print_context_stack_bp(struct thread_info *tinfo,
-                      unsigned long *stack, unsigned long bp,
+                      unsigned long *stack, unsigned long *bp,
                       const struct stacktrace_ops *ops, void *data,
                       unsigned long *end, int *graph);
 
+extern int
+print_context_stack_reliable(struct thread_info *tinfo,
+                            unsigned long *stack, unsigned long *bp,
+                            const struct stacktrace_ops *ops, void *data,
+                            unsigned long *end, int *graph);
+
 /* Generic stack tracer with callbacks */
 
 struct stacktrace_ops {
@@ -43,9 +49,9 @@ struct stacktrace_ops {
        walk_stack_t    walk_stack;
 };
 
-void dump_trace(struct task_struct *tsk, struct pt_regs *regs,
-               unsigned long *stack, unsigned long bp,
-               const struct stacktrace_ops *ops, void *data);
+int dump_trace(struct task_struct *tsk, struct pt_regs *regs,
+              unsigned long *stack, unsigned long bp,
+              const struct stacktrace_ops *ops, void *data);
 
 #ifdef CONFIG_X86_32
 #define STACKSLOTS_PER_LINE 8
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c
index 8efa57a..3b10518 100644
--- a/arch/x86/kernel/dumpstack.c
+++ b/arch/x86/kernel/dumpstack.c
@@ -92,23 +92,22 @@ static inline int valid_stack_ptr(struct thread_info *tinfo,
        return p > t && p < t + THREAD_SIZE - size;
 }
 
-unsigned long
-print_context_stack(struct thread_info *tinfo,
-               unsigned long *stack, unsigned long bp,
-               const struct stacktrace_ops *ops, void *data,
-               unsigned long *end, int *graph)
+int print_context_stack(struct thread_info *tinfo,
+                       unsigned long *stack, unsigned long *bp,
+                       const struct stacktrace_ops *ops, void *data,
+                       unsigned long *end, int *graph)
 {
-       struct stack_frame *frame = (struct stack_frame *)bp;
+       struct stack_frame *frame = (struct stack_frame *)*bp;
 
        while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
                unsigned long addr;
 
                addr = *stack;
                if (__kernel_text_address(addr)) {
-                       if ((unsigned long) stack == bp + sizeof(long)) {
+                       if ((unsigned long) stack == *bp + sizeof(long)) {
                                ops->address(data, addr, 1);
                                frame = frame->next_frame;
-                               bp = (unsigned long) frame;
+                               *bp = (unsigned long) frame;
                        } else {
                                ops->address(data, addr, 0);
                        }
@@ -116,17 +115,16 @@ print_context_stack(struct thread_info *tinfo,
                }
                stack++;
        }
-       return bp;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(print_context_stack);
 
-unsigned long
-print_context_stack_bp(struct thread_info *tinfo,
-                      unsigned long *stack, unsigned long bp,
-                      const struct stacktrace_ops *ops, void *data,
-                      unsigned long *end, int *graph)
+int print_context_stack_bp(struct thread_info *tinfo,
+                          unsigned long *stack, unsigned long *bp,
+                          const struct stacktrace_ops *ops, void *data,
+                          unsigned long *end, int *graph)
 {
-       struct stack_frame *frame = (struct stack_frame *)bp;
+       struct stack_frame *frame = (struct stack_frame *)*bp;
        unsigned long *ret_addr = &frame->return_address;
 
        while (valid_stack_ptr(tinfo, ret_addr, sizeof(*ret_addr), end)) {
@@ -142,7 +140,8 @@ print_context_stack_bp(struct thread_info *tinfo,
                print_ftrace_graph_addr(addr, data, ops, tinfo, graph);
        }
 
-       return (unsigned long)frame;
+       *bp = (unsigned long)frame;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(print_context_stack_bp);
 
diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c
index 464ffd6..e710bab 100644
--- a/arch/x86/kernel/dumpstack_32.c
+++ b/arch/x86/kernel/dumpstack_32.c
@@ -38,13 +38,14 @@ static void *is_softirq_stack(unsigned long *stack, int cpu)
        return is_irq_stack(stack, irq);
 }
 
-void dump_trace(struct task_struct *task, struct pt_regs *regs,
-               unsigned long *stack, unsigned long bp,
-               const struct stacktrace_ops *ops, void *data)
+int dump_trace(struct task_struct *task, struct pt_regs *regs,
+              unsigned long *stack, unsigned long bp,
+              const struct stacktrace_ops *ops, void *data)
 {
        const unsigned cpu = get_cpu();
        int graph = 0;
        u32 *prev_esp;
+       int ret;
 
        if (!task)
                task = current;
@@ -69,8 +70,10 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
                        end_stack = is_softirq_stack(stack, cpu);
 
                context = task_thread_info(task);
-               bp = ops->walk_stack(context, stack, bp, ops, data,
-                                    end_stack, &graph);
+               ret = ops->walk_stack(context, stack, &bp, ops, data,
+                                     end_stack, &graph);
+               if (ret)
+                       goto out;
 
                /* Stop if not on irq stack */
                if (!end_stack)
@@ -82,11 +85,16 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
                if (!stack)
                        break;
 
-               if (ops->stack(data, "IRQ") < 0)
-                       break;
+               ret = ops->stack(data, "IRQ");
+               if (ret)
+                       goto out;
+
                touch_nmi_watchdog();
        }
+
+out:
        put_cpu();
+       return ret;
 }
 EXPORT_SYMBOL(dump_trace);
 
diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c
index 5f1c626..0c810ba 100644
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -148,9 +148,9 @@ analyze_stack(int cpu, struct task_struct *task, unsigned 
long *stack,
  * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
  */
 
-void dump_trace(struct task_struct *task, struct pt_regs *regs,
-               unsigned long *stack, unsigned long bp,
-               const struct stacktrace_ops *ops, void *data)
+int dump_trace(struct task_struct *task, struct pt_regs *regs,
+              unsigned long *stack, unsigned long bp,
+              const struct stacktrace_ops *ops, void *data)
 {
        const unsigned cpu = get_cpu();
        struct thread_info *tinfo;
@@ -159,6 +159,7 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
        unsigned used = 0;
        int graph = 0;
        int done = 0;
+       int ret;
 
        if (!task)
                task = current;
@@ -198,13 +199,18 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
                        break;
 
                case STACK_IS_EXCEPTION:
-
-                       if (ops->stack(data, id) < 0)
-                               break;
-
-                       bp = ops->walk_stack(tinfo, stack, bp, ops,
-                                            data, stack_end, &graph);
-                       ops->stack(data, "<EOE>");
+                       ret = ops->stack(data, id);
+                       if (ret)
+                               goto out;
+
+                       ret = ops->walk_stack(tinfo, stack, &bp, ops, data,
+                                             stack_end, &graph);
+                       if (ret)
+                               goto out;
+
+                       ret = ops->stack(data, "<EOE>");
+                       if (ret)
+                               goto out;
                        /*
                         * We link to the next stack via the
                         * second-to-last pointer (index -2 to end) in the
@@ -215,11 +221,15 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
                        break;
 
                case STACK_IS_IRQ:
+                       ret = ops->stack(data, "IRQ");
+                       if (ret)
+                               goto out;
+
+                       ret = ops->walk_stack(tinfo, stack, &bp, ops, data,
+                                             stack_end, &graph);
+                       if (ret)
+                               goto out;
 
-                       if (ops->stack(data, "IRQ") < 0)
-                               break;
-                       bp = ops->walk_stack(tinfo, stack, bp,
-                                    ops, data, stack_end, &graph);
                        /*
                         * We link to the next stack (which would be
                         * the process stack normally) the last
@@ -227,12 +237,18 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
                         */
                        stack = (unsigned long *) (stack_end[-1]);
                        irq_stack = NULL;
-                       ops->stack(data, "EOI");
+
+                       ret = ops->stack(data, "EOI");
+                       if (ret)
+                               goto out;
+
                        done = 0;
                        break;
 
                case STACK_IS_UNKNOWN:
-                       ops->stack(data, "UNK");
+                       ret = ops->stack(data, "UNK");
+                       if (ret)
+                               goto out;
                        break;
                }
        }
@@ -240,8 +256,11 @@ void dump_trace(struct task_struct *task, struct pt_regs 
*regs,
        /*
         * This handles the process stack:
         */
-       bp = ops->walk_stack(tinfo, stack, bp, ops, data, NULL, &graph);
+       ret = ops->walk_stack(tinfo, stack, &bp, ops, data, NULL, &graph);
+
+out:
        put_cpu();
+       return ret;
 }
 EXPORT_SYMBOL(dump_trace);
 
-- 
2.4.3

Reply via email to