aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/process.c
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@imgtec.com>2016-11-07 10:07:03 -0500
committerRalf Baechle <ralf@linux-mips.org>2017-01-03 10:34:36 -0500
commita3552dace7d1d0cabf573e88fc3025cb90c4a601 (patch)
tree13c516666d5b27ecca831ab9594f02cb5b4c0ec5 /arch/mips/kernel/process.c
parentccaf7caf2c73c6db920772bf08bf1d47b2170634 (diff)
MIPS: Prevent unaligned accesses during stack unwinding
During stack unwinding we call a number of functions to determine what type of instruction we're looking at. The union mips_instruction pointer provided to them may be pointing at a 2 byte, but not 4 byte, aligned address & we thus cannot directly access the 4 byte wide members of the union mips_instruction. To avoid this is_ra_save_ins() copies the required half-words of the microMIPS instruction to a correctly aligned union mips_instruction on the stack, which it can then access safely. The is_jump_ins() & is_sp_move_ins() functions do not correctly perform this temporary copy, and instead attempt to directly dereference 4 byte fields which may be misaligned and lead to an address exception. Fix this by copying the instruction halfwords to a temporary union mips_instruction in get_frame_info() such that we can provide a 4 byte aligned union mips_instruction to the is_*_ins() functions and they do not need to deal with misalignment themselves. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Fixes: 34c2f668d0f6 ("MIPS: microMIPS: Add unaligned access support.") Cc: Leonid Yegoshin <leonid.yegoshin@imgtec.com> Cc: linux-mips@linux-mips.org Cc: <stable@vger.kernel.org> # v3.10+ Patchwork: https://patchwork.linux-mips.org/patch/14529/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r--arch/mips/kernel/process.c70
1 files changed, 35 insertions, 35 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 213278dbbc04..8dddaef1a345 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -199,8 +199,6 @@ struct mips_frame_info {
199static inline int is_ra_save_ins(union mips_instruction *ip) 199static inline int is_ra_save_ins(union mips_instruction *ip)
200{ 200{
201#ifdef CONFIG_CPU_MICROMIPS 201#ifdef CONFIG_CPU_MICROMIPS
202 union mips_instruction mmi;
203
204 /* 202 /*
205 * swsp ra,offset 203 * swsp ra,offset
206 * swm16 reglist,offset(sp) 204 * swm16 reglist,offset(sp)
@@ -210,23 +208,20 @@ static inline int is_ra_save_ins(union mips_instruction *ip)
210 * 208 *
211 * microMIPS is way more fun... 209 * microMIPS is way more fun...
212 */ 210 */
213 if (mm_insn_16bit(ip->halfword[0])) { 211 if (mm_insn_16bit(ip->halfword[1])) {
214 mmi.word = (ip->halfword[0] << 16); 212 return (ip->mm16_r5_format.opcode == mm_swsp16_op &&
215 return (mmi.mm16_r5_format.opcode == mm_swsp16_op && 213 ip->mm16_r5_format.rt == 31) ||
216 mmi.mm16_r5_format.rt == 31) || 214 (ip->mm16_m_format.opcode == mm_pool16c_op &&
217 (mmi.mm16_m_format.opcode == mm_pool16c_op && 215 ip->mm16_m_format.func == mm_swm16_op);
218 mmi.mm16_m_format.func == mm_swm16_op);
219 } 216 }
220 else { 217 else {
221 mmi.halfword[0] = ip->halfword[1]; 218 return (ip->mm_m_format.opcode == mm_pool32b_op &&
222 mmi.halfword[1] = ip->halfword[0]; 219 ip->mm_m_format.rd > 9 &&
223 return (mmi.mm_m_format.opcode == mm_pool32b_op && 220 ip->mm_m_format.base == 29 &&
224 mmi.mm_m_format.rd > 9 && 221 ip->mm_m_format.func == mm_swm32_func) ||
225 mmi.mm_m_format.base == 29 && 222 (ip->i_format.opcode == mm_sw32_op &&
226 mmi.mm_m_format.func == mm_swm32_func) || 223 ip->i_format.rs == 29 &&
227 (mmi.i_format.opcode == mm_sw32_op && 224 ip->i_format.rt == 31);
228 mmi.i_format.rs == 29 &&
229 mmi.i_format.rt == 31);
230 } 225 }
231#else 226#else
232 /* sw / sd $ra, offset($sp) */ 227 /* sw / sd $ra, offset($sp) */
@@ -247,12 +242,8 @@ static inline int is_jump_ins(union mips_instruction *ip)
247 * 242 *
248 * microMIPS is kind of more fun... 243 * microMIPS is kind of more fun...
249 */ 244 */
250 union mips_instruction mmi; 245 if ((ip->mm16_r5_format.opcode == mm_pool16c_op &&
251 246 (ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) ||
252 mmi.word = (ip->halfword[0] << 16);
253
254 if ((mmi.mm16_r5_format.opcode == mm_pool16c_op &&
255 (mmi.mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) ||
256 ip->j_format.opcode == mm_jal32_op) 247 ip->j_format.opcode == mm_jal32_op)
257 return 1; 248 return 1;
258 if (ip->r_format.opcode != mm_pool32a_op || 249 if (ip->r_format.opcode != mm_pool32a_op ||
@@ -281,15 +272,13 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
281 * 272 *
282 * microMIPS is not more fun... 273 * microMIPS is not more fun...
283 */ 274 */
284 if (mm_insn_16bit(ip->halfword[0])) { 275 if (mm_insn_16bit(ip->halfword[1])) {
285 union mips_instruction mmi; 276 return (ip->mm16_r3_format.opcode == mm_pool16d_op &&
286 277 ip->mm16_r3_format.simmediate && mm_addiusp_func) ||
287 mmi.word = (ip->halfword[0] << 16); 278 (ip->mm16_r5_format.opcode == mm_pool16d_op &&
288 return (mmi.mm16_r3_format.opcode == mm_pool16d_op && 279 ip->mm16_r5_format.rt == 29);
289 mmi.mm16_r3_format.simmediate && mm_addiusp_func) ||
290 (mmi.mm16_r5_format.opcode == mm_pool16d_op &&
291 mmi.mm16_r5_format.rt == 29);
292 } 280 }
281
293 return ip->mm_i_format.opcode == mm_addiu32_op && 282 return ip->mm_i_format.opcode == mm_addiu32_op &&
294 ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29; 283 ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29;
295#else 284#else
@@ -304,7 +293,8 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
304 293
305static int get_frame_info(struct mips_frame_info *info) 294static int get_frame_info(struct mips_frame_info *info)
306{ 295{
307 union mips_instruction *ip; 296 bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS);
297 union mips_instruction insn, *ip;
308 unsigned max_insns = info->func_size / sizeof(union mips_instruction); 298 unsigned max_insns = info->func_size / sizeof(union mips_instruction);
309 unsigned i; 299 unsigned i;
310 300
@@ -320,11 +310,21 @@ static int get_frame_info(struct mips_frame_info *info)
320 max_insns = min(128U, max_insns); 310 max_insns = min(128U, max_insns);
321 311
322 for (i = 0; i < max_insns; i++, ip++) { 312 for (i = 0; i < max_insns; i++, ip++) {
313 if (is_mmips && mm_insn_16bit(ip->halfword[0])) {
314 insn.halfword[0] = 0;
315 insn.halfword[1] = ip->halfword[0];
316 } else if (is_mmips) {
317 insn.halfword[0] = ip->halfword[1];
318 insn.halfword[1] = ip->halfword[0];
319 } else {
320 insn.word = ip->word;
321 }
323 322
324 if (is_jump_ins(ip)) 323 if (is_jump_ins(&insn))
325 break; 324 break;
325
326 if (!info->frame_size) { 326 if (!info->frame_size) {
327 if (is_sp_move_ins(ip)) 327 if (is_sp_move_ins(&insn))
328 { 328 {
329#ifdef CONFIG_CPU_MICROMIPS 329#ifdef CONFIG_CPU_MICROMIPS
330 if (mm_insn_16bit(ip->halfword[0])) 330 if (mm_insn_16bit(ip->halfword[0]))
@@ -347,7 +347,7 @@ static int get_frame_info(struct mips_frame_info *info)
347 } 347 }
348 continue; 348 continue;
349 } 349 }
350 if (info->pc_offset == -1 && is_ra_save_ins(ip)) { 350 if (info->pc_offset == -1 && is_ra_save_ins(&insn)) {
351 info->pc_offset = 351 info->pc_offset =
352 ip->i_format.simmediate / sizeof(long); 352 ip->i_format.simmediate / sizeof(long);
353 break; 353 break;