aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Rutland <mark.rutland@arm.com>2017-11-02 13:44:28 -0400
committerRussell King <rmk+kernel@armlinux.org.uk>2017-11-06 07:00:38 -0500
commitb9dd05c7002ee0ca8b676428b2268c26399b5e31 (patch)
tree726645a75aa6e091da0ea05daa1c64f5d619bdf5
parentdad4675388fcb4353aea64174a165fb8494f1c13 (diff)
ARM: 8720/1: ensure dump_instr() checks addr_limit
When CONFIG_DEBUG_USER is enabled, it's possible for a user to deliberately trigger dump_instr() with a chosen kernel address. Let's avoid problems resulting from this by using get_user() rather than __get_user(), ensuring that we don't erroneously access kernel memory. So that we can use the same code to dump user instructions and kernel instructions, the common dumping code is factored out to __dump_instr(), with the fs manipulated appropriately in dump_instr() around calls to this. Signed-off-by: Mark Rutland <mark.rutland@arm.com> Cc: stable@vger.kernel.org Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--arch/arm/kernel/traps.c28
1 files changed, 18 insertions, 10 deletions
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 948c648fea00..0fcd82f01388 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -154,30 +154,26 @@ static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
154 set_fs(fs); 154 set_fs(fs);
155} 155}
156 156
157static void dump_instr(const char *lvl, struct pt_regs *regs) 157static void __dump_instr(const char *lvl, struct pt_regs *regs)
158{ 158{
159 unsigned long addr = instruction_pointer(regs); 159 unsigned long addr = instruction_pointer(regs);
160 const int thumb = thumb_mode(regs); 160 const int thumb = thumb_mode(regs);
161 const int width = thumb ? 4 : 8; 161 const int width = thumb ? 4 : 8;
162 mm_segment_t fs;
163 char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str; 162 char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
164 int i; 163 int i;
165 164
166 /* 165 /*
167 * We need to switch to kernel mode so that we can use __get_user 166 * Note that we now dump the code first, just in case the backtrace
168 * to safely read from kernel space. Note that we now dump the 167 * kills us.
169 * code first, just in case the backtrace kills us.
170 */ 168 */
171 fs = get_fs();
172 set_fs(KERNEL_DS);
173 169
174 for (i = -4; i < 1 + !!thumb; i++) { 170 for (i = -4; i < 1 + !!thumb; i++) {
175 unsigned int val, bad; 171 unsigned int val, bad;
176 172
177 if (thumb) 173 if (thumb)
178 bad = __get_user(val, &((u16 *)addr)[i]); 174 bad = get_user(val, &((u16 *)addr)[i]);
179 else 175 else
180 bad = __get_user(val, &((u32 *)addr)[i]); 176 bad = get_user(val, &((u32 *)addr)[i]);
181 177
182 if (!bad) 178 if (!bad)
183 p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ", 179 p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
@@ -188,8 +184,20 @@ static void dump_instr(const char *lvl, struct pt_regs *regs)
188 } 184 }
189 } 185 }
190 printk("%sCode: %s\n", lvl, str); 186 printk("%sCode: %s\n", lvl, str);
187}
191 188
192 set_fs(fs); 189static void dump_instr(const char *lvl, struct pt_regs *regs)
190{
191 mm_segment_t fs;
192
193 if (!user_mode(regs)) {
194 fs = get_fs();
195 set_fs(KERNEL_DS);
196 __dump_instr(lvl, regs);
197 set_fs(fs);
198 } else {
199 __dump_instr(lvl, regs);
200 }
193} 201}
194 202
195#ifdef CONFIG_ARM_UNWIND 203#ifdef CONFIG_ARM_UNWIND