| From 477b55e9136344ec5dd8c7cf2ab64fc089fbfe6b Mon Sep 17 00:00:00 2001 |
| From: Matt Redfearn <matt.redfearn@imgtec.com> |
| Date: Tue, 8 Aug 2017 13:22:30 +0100 |
| Subject: MIPS: Handle non word sized instructions when examining frame |
| |
| [ Upstream commit 11887ed172a6960673f130dad8f8fb42778f64d7 ] |
| |
| Commit 34c2f668d0f6b ("MIPS: microMIPS: Add unaligned access support.") |
| added fairly broken support for handling 16bit microMIPS instructions in |
| get_frame_info(). It adjusts the instruction pointer by 16bits in the |
| case of a 16bit sp move instruction, but not any other 16bit |
| instruction. |
| |
| Commit b6c7a324df37 ("MIPS: Fix get_frame_info() handling of microMIPS |
| function size") goes some way to fixing get_frame_info() to iterate over |
| microMIPS instuctions, but the instruction pointer is still manipulated |
| using a postincrement, and is of union mips_instruction type. Since the |
| union is sized to the largest member (a word), but microMIPS |
| instructions are a mix of halfword and word sizes, the function does not |
| always iterate correctly, ending up misaligned with the instruction |
| stream and interpreting it incorrectly. |
| |
| Since the instruction modifying the stack pointer is usually the first |
| in the function, that one is usually handled correctly. But the |
| instruction which saves the return address to the sp is some variable |
| number of instructions into the frame and is frequently missed due to |
| not being on a word boundary, leading to incomplete walking of the |
| stack. |
| |
| Fix this by incrementing the instruction pointer based on the size of |
| the previously decoded instruction (& remove the hack introduced by |
| commit 34c2f668d0f6b ("MIPS: microMIPS: Add unaligned access support.") |
| which adjusts the instruction pointer in the case of a 16bit sp move |
| instruction, but not any other). |
| |
| Fixes: 34c2f668d0f6b ("MIPS: microMIPS: Add unaligned access support.") |
| Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com> |
| Cc: Marcin Nowakowski <marcin.nowakowski@imgtec.com> |
| Cc: James Hogan <james.hogan@imgtec.com> |
| Cc: Ingo Molnar <mingo@kernel.org> |
| Cc: Paul Burton <paul.burton@imgtec.com> |
| Cc: linux-mips@linux-mips.org |
| Cc: linux-kernel@vger.kernel.org |
| Patchwork: https://patchwork.linux-mips.org/patch/16953/ |
| Signed-off-by: Ralf Baechle <ralf@linux-mips.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/mips/kernel/process.c | 9 ++++++--- |
| 1 file changed, 6 insertions(+), 3 deletions(-) |
| |
| diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c |
| index 0211dc737a21..1cc133e7026f 100644 |
| --- a/arch/mips/kernel/process.c |
| +++ b/arch/mips/kernel/process.c |
| @@ -346,6 +346,7 @@ static int get_frame_info(struct mips_frame_info *info) |
| bool is_mmips = IS_ENABLED(CONFIG_CPU_MICROMIPS); |
| union mips_instruction insn, *ip, *ip_end; |
| const unsigned int max_insns = 128; |
| + unsigned int last_insn_size = 0; |
| unsigned int i; |
| |
| info->pc_offset = -1; |
| @@ -357,15 +358,19 @@ static int get_frame_info(struct mips_frame_info *info) |
| |
| ip_end = (void *)ip + info->func_size; |
| |
| - for (i = 0; i < max_insns && ip < ip_end; i++, ip++) { |
| + for (i = 0; i < max_insns && ip < ip_end; i++) { |
| + ip = (void *)ip + last_insn_size; |
| if (is_mmips && mm_insn_16bit(ip->halfword[0])) { |
| insn.halfword[0] = 0; |
| insn.halfword[1] = ip->halfword[0]; |
| + last_insn_size = 2; |
| } else if (is_mmips) { |
| insn.halfword[0] = ip->halfword[1]; |
| insn.halfword[1] = ip->halfword[0]; |
| + last_insn_size = 4; |
| } else { |
| insn.word = ip->word; |
| + last_insn_size = 4; |
| } |
| |
| if (is_jump_ins(&insn)) |
| @@ -387,8 +392,6 @@ static int get_frame_info(struct mips_frame_info *info) |
| tmp = (ip->halfword[0] >> 1); |
| info->frame_size = -(signed short)(tmp & 0xf); |
| } |
| - ip = (void *) &ip->halfword[1]; |
| - ip--; |
| } else |
| #endif |
| info->frame_size = - ip->i_format.simmediate; |
| -- |
| 2.17.1 |
| |