Skip to content

Commit a3552da

Browse files
paulburtonralfbaechle
authored andcommitted
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: 34c2f66 ("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>
1 parent ccaf7ca commit a3552da

File tree

1 file changed

+35
-35
lines changed

1 file changed

+35
-35
lines changed

arch/mips/kernel/process.c

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,6 @@ struct mips_frame_info {
199199
static inline int is_ra_save_ins(union mips_instruction *ip)
200200
{
201201
#ifdef CONFIG_CPU_MICROMIPS
202-
union mips_instruction mmi;
203-
204202
/*
205203
* swsp ra,offset
206204
* swm16 reglist,offset(sp)
@@ -210,23 +208,20 @@ static inline int is_ra_save_ins(union mips_instruction *ip)
210208
*
211209
* microMIPS is way more fun...
212210
*/
213-
if (mm_insn_16bit(ip->halfword[0])) {
214-
mmi.word = (ip->halfword[0] << 16);
215-
return (mmi.mm16_r5_format.opcode == mm_swsp16_op &&
216-
mmi.mm16_r5_format.rt == 31) ||
217-
(mmi.mm16_m_format.opcode == mm_pool16c_op &&
218-
mmi.mm16_m_format.func == mm_swm16_op);
211+
if (mm_insn_16bit(ip->halfword[1])) {
212+
return (ip->mm16_r5_format.opcode == mm_swsp16_op &&
213+
ip->mm16_r5_format.rt == 31) ||
214+
(ip->mm16_m_format.opcode == mm_pool16c_op &&
215+
ip->mm16_m_format.func == mm_swm16_op);
219216
}
220217
else {
221-
mmi.halfword[0] = ip->halfword[1];
222-
mmi.halfword[1] = ip->halfword[0];
223-
return (mmi.mm_m_format.opcode == mm_pool32b_op &&
224-
mmi.mm_m_format.rd > 9 &&
225-
mmi.mm_m_format.base == 29 &&
226-
mmi.mm_m_format.func == mm_swm32_func) ||
227-
(mmi.i_format.opcode == mm_sw32_op &&
228-
mmi.i_format.rs == 29 &&
229-
mmi.i_format.rt == 31);
218+
return (ip->mm_m_format.opcode == mm_pool32b_op &&
219+
ip->mm_m_format.rd > 9 &&
220+
ip->mm_m_format.base == 29 &&
221+
ip->mm_m_format.func == mm_swm32_func) ||
222+
(ip->i_format.opcode == mm_sw32_op &&
223+
ip->i_format.rs == 29 &&
224+
ip->i_format.rt == 31);
230225
}
231226
#else
232227
/* sw / sd $ra, offset($sp) */
@@ -247,12 +242,8 @@ static inline int is_jump_ins(union mips_instruction *ip)
247242
*
248243
* microMIPS is kind of more fun...
249244
*/
250-
union mips_instruction mmi;
251-
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) ||
245+
if ((ip->mm16_r5_format.opcode == mm_pool16c_op &&
246+
(ip->mm16_r5_format.rt & mm_jr16_op) == mm_jr16_op) ||
256247
ip->j_format.opcode == mm_jal32_op)
257248
return 1;
258249
if (ip->r_format.opcode != mm_pool32a_op ||
@@ -281,15 +272,13 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
281272
*
282273
* microMIPS is not more fun...
283274
*/
284-
if (mm_insn_16bit(ip->halfword[0])) {
285-
union mips_instruction mmi;
286-
287-
mmi.word = (ip->halfword[0] << 16);
288-
return (mmi.mm16_r3_format.opcode == mm_pool16d_op &&
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);
275+
if (mm_insn_16bit(ip->halfword[1])) {
276+
return (ip->mm16_r3_format.opcode == mm_pool16d_op &&
277+
ip->mm16_r3_format.simmediate && mm_addiusp_func) ||
278+
(ip->mm16_r5_format.opcode == mm_pool16d_op &&
279+
ip->mm16_r5_format.rt == 29);
292280
}
281+
293282
return ip->mm_i_format.opcode == mm_addiu32_op &&
294283
ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29;
295284
#else
@@ -304,7 +293,8 @@ static inline int is_sp_move_ins(union mips_instruction *ip)
304293

305294
static int get_frame_info(struct mips_frame_info *info)
306295
{
307-
union mips_instruction *ip;
296+
bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS);
297+
union mips_instruction insn, *ip;
308298
unsigned max_insns = info->func_size / sizeof(union mips_instruction);
309299
unsigned i;
310300

@@ -320,11 +310,21 @@ static int get_frame_info(struct mips_frame_info *info)
320310
max_insns = min(128U, max_insns);
321311

322312
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+
}
323322

324-
if (is_jump_ins(ip))
323+
if (is_jump_ins(&insn))
325324
break;
325+
326326
if (!info->frame_size) {
327-
if (is_sp_move_ins(ip))
327+
if (is_sp_move_ins(&insn))
328328
{
329329
#ifdef CONFIG_CPU_MICROMIPS
330330
if (mm_insn_16bit(ip->halfword[0]))
@@ -347,7 +347,7 @@ static int get_frame_info(struct mips_frame_info *info)
347347
}
348348
continue;
349349
}
350-
if (info->pc_offset == -1 && is_ra_save_ins(ip)) {
350+
if (info->pc_offset == -1 && is_ra_save_ins(&insn)) {
351351
info->pc_offset =
352352
ip->i_format.simmediate / sizeof(long);
353353
break;

0 commit comments

Comments
 (0)