diff options
author | Paul Burton <paul.burton@imgtec.com> | 2016-11-07 10:07:03 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2017-01-03 10:34:36 -0500 |
commit | a3552dace7d1d0cabf573e88fc3025cb90c4a601 (patch) | |
tree | 13c516666d5b27ecca831ab9594f02cb5b4c0ec5 /arch/mips/kernel/process.c | |
parent | ccaf7caf2c73c6db920772bf08bf1d47b2170634 (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.c | 70 |
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 { | |||
199 | static inline int is_ra_save_ins(union mips_instruction *ip) | 199 | static 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 | ||
305 | static int get_frame_info(struct mips_frame_info *info) | 294 | static 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; |