aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-08-31 23:18:51 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-08-31 23:18:51 -0400
commit36ad4885c47c2187822f2783fb46fde2d36bf200 (patch)
tree3cdc924a89a6624e685ef1b3926ecab8c54ffaf5
parent3b2b64fd311c92f2137eb7cee7025794cd854057 (diff)
x86: be even more careful about checking the stack frame on dumping
lguest didn't initialize the kernel stack the way a real i386 kernel does, and ended up triggering a corner-case in the stack frame checking that doesn't happen on naive i386, and that the stack dumping didn't handle quite right. This makes the frame handling more correct, and tries to clarify the code at the same time so that it's a bit more obvious what is going on. Thanks to Rusty Russell for debugging the lguest failure- Cc: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/i386/kernel/traps.c33
1 files changed, 21 insertions, 12 deletions
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index cfffe3dd9e83..47b0bef335bd 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -100,36 +100,45 @@ asmlinkage void machine_check(void);
100int kstack_depth_to_print = 24; 100int kstack_depth_to_print = 24;
101static unsigned int code_bytes = 64; 101static unsigned int code_bytes = 64;
102 102
103static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) 103static inline int valid_stack_ptr(struct thread_info *tinfo, void *p, unsigned size)
104{ 104{
105 return p > (void *)tinfo && 105 return p > (void *)tinfo &&
106 p < (void *)tinfo + THREAD_SIZE - 3; 106 p <= (void *)tinfo + THREAD_SIZE - size;
107} 107}
108 108
109/* The form of the top of the frame on the stack */
110struct stack_frame {
111 struct stack_frame *next_frame;
112 unsigned long return_address;
113};
114
109static inline unsigned long print_context_stack(struct thread_info *tinfo, 115static inline unsigned long print_context_stack(struct thread_info *tinfo,
110 unsigned long *stack, unsigned long ebp, 116 unsigned long *stack, unsigned long ebp,
111 struct stacktrace_ops *ops, void *data) 117 struct stacktrace_ops *ops, void *data)
112{ 118{
113 unsigned long addr;
114
115#ifdef CONFIG_FRAME_POINTER 119#ifdef CONFIG_FRAME_POINTER
116 while (valid_stack_ptr(tinfo, (void *)ebp)) { 120 struct stack_frame *frame = (struct stack_frame *)ebp;
117 unsigned long new_ebp; 121 while (valid_stack_ptr(tinfo, frame, sizeof(*frame))) {
118 addr = *(unsigned long *)(ebp + 4); 122 struct stack_frame *next;
123 unsigned long addr;
124
125 addr = frame->return_address;
119 ops->address(data, addr); 126 ops->address(data, addr);
120 /* 127 /*
121 * break out of recursive entries (such as 128 * break out of recursive entries (such as
122 * end_of_stack_stop_unwind_function). Also, 129 * end_of_stack_stop_unwind_function). Also,
123 * we can never allow a frame pointer to 130 * we can never allow a frame pointer to
124 * move downwards! 131 * move downwards!
125 */ 132 */
126 new_ebp = *(unsigned long *)ebp; 133 next = frame->next_frame;
127 if (new_ebp <= ebp) 134 if (next <= frame)
128 break; 135 break;
129 ebp = new_ebp; 136 frame = next;
130 } 137 }
131#else 138#else
132 while (valid_stack_ptr(tinfo, stack)) { 139 while (valid_stack_ptr(tinfo, stack, sizeof(*stack))) {
140 unsigned long addr;
141
133 addr = *stack++; 142 addr = *stack++;
134 if (__kernel_text_address(addr)) 143 if (__kernel_text_address(addr))
135 ops->address(data, addr); 144 ops->address(data, addr);