diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-08-31 23:18:51 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-08-31 23:18:51 -0400 |
commit | 36ad4885c47c2187822f2783fb46fde2d36bf200 (patch) | |
tree | 3cdc924a89a6624e685ef1b3926ecab8c54ffaf5 /arch/i386 | |
parent | 3b2b64fd311c92f2137eb7cee7025794cd854057 (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>
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/traps.c | 33 |
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); | |||
100 | int kstack_depth_to_print = 24; | 100 | int kstack_depth_to_print = 24; |
101 | static unsigned int code_bytes = 64; | 101 | static unsigned int code_bytes = 64; |
102 | 102 | ||
103 | static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) | 103 | static 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 */ | ||
110 | struct stack_frame { | ||
111 | struct stack_frame *next_frame; | ||
112 | unsigned long return_address; | ||
113 | }; | ||
114 | |||
109 | static inline unsigned long print_context_stack(struct thread_info *tinfo, | 115 | static 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); |