diff options
author | Helge Deller <deller@gmx.de> | 2008-11-26 15:46:22 -0500 |
---|---|---|
committer | Kyle McMartin <kyle@hera.kernel.org> | 2008-11-26 17:22:39 -0500 |
commit | 7a3f5134a8f5bd7fa38b5645eef05e8a4eb62951 (patch) | |
tree | 565fb518a0eb19f0514e77ff0124be3642f281f2 | |
parent | 9860d1b08b082ffb54c4b7827c48c2728e12ba21 (diff) |
parisc: fix kernel crash when unwinding a userspace process
Any user on existing parisc 32- and 64bit-kernels can easily crash
the kernel and as such enforce a DSO.
A simple testcase is available here:
http://gsyprf10.external.hp.com/~deller/crash.tgz
The problem is introduced by the fact, that the handle_interruption()
crash handler calls the show_regs() function, which in turn tries to
unwind the stack by calling parisc_show_stack(). Since the stack contains
userspace addresses, a try to unwind the stack is dangerous and useless
and leads to the crash.
The fix is trivial: For userspace processes
a) avoid to unwind the stack, and
b) avoid to resolve userspace addresses to kernel symbol names.
While touching this code, I converted print_symbol() to %pS
printk formats and made parisc_show_stack() static.
An initial patch for this was written by Kyle McMartin back in August:
http://marc.info/?l=linux-parisc&m=121805168830283&w=2
Compile and run-tested with a 64bit parisc kernel.
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: Grant Grundler <grundler@parisc-linux.org>
Cc: Matthew Wilcox <matthew@wil.cx>
Cc: <stable@kernel.org> [2.6.25.x, 2.6.26.x, 2.6.27.x, earlier...]
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Kyle McMartin <kyle@mcmartin.ca>
-rw-r--r-- | arch/parisc/kernel/traps.c | 43 |
1 files changed, 21 insertions, 22 deletions
diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index 675f1d098f05..4c771cd580ec 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <linux/init.h> | 24 | #include <linux/init.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/console.h> | 26 | #include <linux/console.h> |
27 | #include <linux/kallsyms.h> | ||
28 | #include <linux/bug.h> | 27 | #include <linux/bug.h> |
29 | 28 | ||
30 | #include <asm/assembly.h> | 29 | #include <asm/assembly.h> |
@@ -51,7 +50,7 @@ | |||
51 | DEFINE_SPINLOCK(pa_dbit_lock); | 50 | DEFINE_SPINLOCK(pa_dbit_lock); |
52 | #endif | 51 | #endif |
53 | 52 | ||
54 | void parisc_show_stack(struct task_struct *t, unsigned long *sp, | 53 | static void parisc_show_stack(struct task_struct *task, unsigned long *sp, |
55 | struct pt_regs *regs); | 54 | struct pt_regs *regs); |
56 | 55 | ||
57 | static int printbinary(char *buf, unsigned long x, int nbits) | 56 | static int printbinary(char *buf, unsigned long x, int nbits) |
@@ -121,18 +120,19 @@ static void print_fr(char *level, struct pt_regs *regs) | |||
121 | 120 | ||
122 | void show_regs(struct pt_regs *regs) | 121 | void show_regs(struct pt_regs *regs) |
123 | { | 122 | { |
124 | int i; | 123 | int i, user; |
125 | char *level; | 124 | char *level; |
126 | unsigned long cr30, cr31; | 125 | unsigned long cr30, cr31; |
127 | 126 | ||
128 | level = user_mode(regs) ? KERN_DEBUG : KERN_CRIT; | 127 | user = user_mode(regs); |
128 | level = user ? KERN_DEBUG : KERN_CRIT; | ||
129 | 129 | ||
130 | print_gr(level, regs); | 130 | print_gr(level, regs); |
131 | 131 | ||
132 | for (i = 0; i < 8; i += 4) | 132 | for (i = 0; i < 8; i += 4) |
133 | PRINTREGS(level, regs->sr, "sr", RFMT, i); | 133 | PRINTREGS(level, regs->sr, "sr", RFMT, i); |
134 | 134 | ||
135 | if (user_mode(regs)) | 135 | if (user) |
136 | print_fr(level, regs); | 136 | print_fr(level, regs); |
137 | 137 | ||
138 | cr30 = mfctl(30); | 138 | cr30 = mfctl(30); |
@@ -145,14 +145,18 @@ void show_regs(struct pt_regs *regs) | |||
145 | printk("%s CPU: %8d CR30: " RFMT " CR31: " RFMT "\n", | 145 | printk("%s CPU: %8d CR30: " RFMT " CR31: " RFMT "\n", |
146 | level, current_thread_info()->cpu, cr30, cr31); | 146 | level, current_thread_info()->cpu, cr30, cr31); |
147 | printk("%s ORIG_R28: " RFMT "\n", level, regs->orig_r28); | 147 | printk("%s ORIG_R28: " RFMT "\n", level, regs->orig_r28); |
148 | printk(level); | 148 | |
149 | print_symbol(" IAOQ[0]: %s\n", regs->iaoq[0]); | 149 | if (user) { |
150 | printk(level); | 150 | printk("%s IAOQ[0]: " RFMT "\n", level, regs->iaoq[0]); |
151 | print_symbol(" IAOQ[1]: %s\n", regs->iaoq[1]); | 151 | printk("%s IAOQ[1]: " RFMT "\n", level, regs->iaoq[1]); |
152 | printk(level); | 152 | printk("%s RP(r2): " RFMT "\n", level, regs->gr[2]); |
153 | print_symbol(" RP(r2): %s\n", regs->gr[2]); | 153 | } else { |
154 | 154 | printk("%s IAOQ[0]: %pS\n", level, (void *) regs->iaoq[0]); | |
155 | parisc_show_stack(current, NULL, regs); | 155 | printk("%s IAOQ[1]: %pS\n", level, (void *) regs->iaoq[1]); |
156 | printk("%s RP(r2): %pS\n", level, (void *) regs->gr[2]); | ||
157 | |||
158 | parisc_show_stack(current, NULL, regs); | ||
159 | } | ||
156 | } | 160 | } |
157 | 161 | ||
158 | 162 | ||
@@ -173,20 +177,15 @@ static void do_show_stack(struct unwind_frame_info *info) | |||
173 | break; | 177 | break; |
174 | 178 | ||
175 | if (__kernel_text_address(info->ip)) { | 179 | if (__kernel_text_address(info->ip)) { |
176 | printk("%s [<" RFMT ">] ", (i&0x3)==1 ? KERN_CRIT : "", info->ip); | 180 | printk(KERN_CRIT " [<" RFMT ">] %pS\n", |
177 | #ifdef CONFIG_KALLSYMS | 181 | info->ip, (void *) info->ip); |
178 | print_symbol("%s\n", info->ip); | ||
179 | #else | ||
180 | if ((i & 0x03) == 0) | ||
181 | printk("\n"); | ||
182 | #endif | ||
183 | i++; | 182 | i++; |
184 | } | 183 | } |
185 | } | 184 | } |
186 | printk("\n"); | 185 | printk(KERN_CRIT "\n"); |
187 | } | 186 | } |
188 | 187 | ||
189 | void parisc_show_stack(struct task_struct *task, unsigned long *sp, | 188 | static void parisc_show_stack(struct task_struct *task, unsigned long *sp, |
190 | struct pt_regs *regs) | 189 | struct pt_regs *regs) |
191 | { | 190 | { |
192 | struct unwind_frame_info info; | 191 | struct unwind_frame_info info; |