diff options
| -rw-r--r-- | arch/x86/include/asm/unwind.h | 17 | ||||
| -rw-r--r-- | arch/x86/kernel/dumpstack.c | 28 | ||||
| -rw-r--r-- | arch/x86/kernel/stacktrace.c | 2 |
3 files changed, 34 insertions, 13 deletions
diff --git a/arch/x86/include/asm/unwind.h b/arch/x86/include/asm/unwind.h index c1688c2d0a12..1f86e1b0a5cd 100644 --- a/arch/x86/include/asm/unwind.h +++ b/arch/x86/include/asm/unwind.h | |||
| @@ -56,18 +56,27 @@ void unwind_start(struct unwind_state *state, struct task_struct *task, | |||
| 56 | 56 | ||
| 57 | #if defined(CONFIG_UNWINDER_ORC) || defined(CONFIG_UNWINDER_FRAME_POINTER) | 57 | #if defined(CONFIG_UNWINDER_ORC) || defined(CONFIG_UNWINDER_FRAME_POINTER) |
| 58 | /* | 58 | /* |
| 59 | * WARNING: The entire pt_regs may not be safe to dereference. In some cases, | 59 | * If 'partial' returns true, only the iret frame registers are valid. |
| 60 | * only the iret frame registers are accessible. Use with caution! | ||
| 61 | */ | 60 | */ |
| 62 | static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state) | 61 | static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state, |
| 62 | bool *partial) | ||
| 63 | { | 63 | { |
| 64 | if (unwind_done(state)) | 64 | if (unwind_done(state)) |
| 65 | return NULL; | 65 | return NULL; |
| 66 | 66 | ||
| 67 | if (partial) { | ||
| 68 | #ifdef CONFIG_UNWINDER_ORC | ||
| 69 | *partial = !state->full_regs; | ||
| 70 | #else | ||
| 71 | *partial = false; | ||
| 72 | #endif | ||
| 73 | } | ||
| 74 | |||
| 67 | return state->regs; | 75 | return state->regs; |
| 68 | } | 76 | } |
| 69 | #else | 77 | #else |
| 70 | static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state) | 78 | static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state, |
| 79 | bool *partial) | ||
| 71 | { | 80 | { |
| 72 | return NULL; | 81 | return NULL; |
| 73 | } | 82 | } |
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 5fa110699ed2..d0bb176a7261 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
| @@ -76,12 +76,23 @@ void show_iret_regs(struct pt_regs *regs) | |||
| 76 | regs->sp, regs->flags); | 76 | regs->sp, regs->flags); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | static void show_regs_safe(struct stack_info *info, struct pt_regs *regs) | 79 | static void show_regs_if_on_stack(struct stack_info *info, struct pt_regs *regs, |
| 80 | bool partial) | ||
| 80 | { | 81 | { |
| 81 | if (on_stack(info, regs, sizeof(*regs))) | 82 | /* |
| 83 | * These on_stack() checks aren't strictly necessary: the unwind code | ||
| 84 | * has already validated the 'regs' pointer. The checks are done for | ||
| 85 | * ordering reasons: if the registers are on the next stack, we don't | ||
| 86 | * want to print them out yet. Otherwise they'll be shown as part of | ||
| 87 | * the wrong stack. Later, when show_trace_log_lvl() switches to the | ||
| 88 | * next stack, this function will be called again with the same regs so | ||
| 89 | * they can be printed in the right context. | ||
| 90 | */ | ||
| 91 | if (!partial && on_stack(info, regs, sizeof(*regs))) { | ||
| 82 | __show_regs(regs, 0); | 92 | __show_regs(regs, 0); |
| 83 | else if (on_stack(info, (void *)regs + IRET_FRAME_OFFSET, | 93 | |
| 84 | IRET_FRAME_SIZE)) { | 94 | } else if (partial && on_stack(info, (void *)regs + IRET_FRAME_OFFSET, |
| 95 | IRET_FRAME_SIZE)) { | ||
| 85 | /* | 96 | /* |
| 86 | * When an interrupt or exception occurs in entry code, the | 97 | * When an interrupt or exception occurs in entry code, the |
| 87 | * full pt_regs might not have been saved yet. In that case | 98 | * full pt_regs might not have been saved yet. In that case |
| @@ -98,6 +109,7 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
| 98 | struct stack_info stack_info = {0}; | 109 | struct stack_info stack_info = {0}; |
| 99 | unsigned long visit_mask = 0; | 110 | unsigned long visit_mask = 0; |
| 100 | int graph_idx = 0; | 111 | int graph_idx = 0; |
| 112 | bool partial; | ||
| 101 | 113 | ||
| 102 | printk("%sCall Trace:\n", log_lvl); | 114 | printk("%sCall Trace:\n", log_lvl); |
| 103 | 115 | ||
| @@ -140,7 +152,7 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
| 140 | printk("%s <%s>\n", log_lvl, stack_name); | 152 | printk("%s <%s>\n", log_lvl, stack_name); |
| 141 | 153 | ||
| 142 | if (regs) | 154 | if (regs) |
| 143 | show_regs_safe(&stack_info, regs); | 155 | show_regs_if_on_stack(&stack_info, regs, partial); |
| 144 | 156 | ||
| 145 | /* | 157 | /* |
| 146 | * Scan the stack, printing any text addresses we find. At the | 158 | * Scan the stack, printing any text addresses we find. At the |
| @@ -164,7 +176,7 @@ void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, | |||
| 164 | 176 | ||
| 165 | /* | 177 | /* |
| 166 | * Don't print regs->ip again if it was already printed | 178 | * Don't print regs->ip again if it was already printed |
| 167 | * by show_regs_safe() below. | 179 | * by show_regs_if_on_stack(). |
| 168 | */ | 180 | */ |
| 169 | if (regs && stack == ®s->ip) | 181 | if (regs && stack == ®s->ip) |
| 170 | goto next; | 182 | goto next; |
| @@ -199,9 +211,9 @@ next: | |||
| 199 | unwind_next_frame(&state); | 211 | unwind_next_frame(&state); |
| 200 | 212 | ||
| 201 | /* if the frame has entry regs, print them */ | 213 | /* if the frame has entry regs, print them */ |
| 202 | regs = unwind_get_entry_regs(&state); | 214 | regs = unwind_get_entry_regs(&state, &partial); |
| 203 | if (regs) | 215 | if (regs) |
| 204 | show_regs_safe(&stack_info, regs); | 216 | show_regs_if_on_stack(&stack_info, regs, partial); |
| 205 | } | 217 | } |
| 206 | 218 | ||
| 207 | if (stack_name) | 219 | if (stack_name) |
diff --git a/arch/x86/kernel/stacktrace.c b/arch/x86/kernel/stacktrace.c index 8dabd7bf1673..60244bfaf88f 100644 --- a/arch/x86/kernel/stacktrace.c +++ b/arch/x86/kernel/stacktrace.c | |||
| @@ -98,7 +98,7 @@ static int __save_stack_trace_reliable(struct stack_trace *trace, | |||
| 98 | for (unwind_start(&state, task, NULL, NULL); !unwind_done(&state); | 98 | for (unwind_start(&state, task, NULL, NULL); !unwind_done(&state); |
| 99 | unwind_next_frame(&state)) { | 99 | unwind_next_frame(&state)) { |
| 100 | 100 | ||
| 101 | regs = unwind_get_entry_regs(&state); | 101 | regs = unwind_get_entry_regs(&state, NULL); |
| 102 | if (regs) { | 102 | if (regs) { |
| 103 | /* | 103 | /* |
| 104 | * Kernel mode registers on the stack indicate an | 104 | * Kernel mode registers on the stack indicate an |
