aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViktor Rosendahl <viktor.rosendahl@nokia.com>2011-03-26 13:11:01 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2011-03-28 14:01:16 -0400
commit0652f067360fc3be2c3e668085f5fb2b76402928 (patch)
tree5ce7272e70266f50a0701bd6d1ce13abd4d451b4
parent255bae73b214f143a3c7cc74d4792eb166a10d46 (diff)
ARM: 6836/1: kprobes/fix emulation of LDR/STR instruction when Rn == PC
The Rn value from the emulation is unconditionally written back; this is fine as long as Rn != PC because in that case, even if the instruction isn't a write back instruction, it will only result in the same value being written back. In case Rn == PC, then the emulated instruction doesn't have the actual PC value in Rn but an adjusted value; when this is written back, it will result in the PC being incorrectly updated. An altenative solution would be to check bits 24 and 22 to see whether the instruction actually is a write back instruction or not. I think it's enough to check whether Rn != PC, because: - it's looks cheaper than the alternative - to my understaning it's not permitted to update the PC with a write back instruction, so we don't lose any ability to emulate legal instructions. - in case of writing back for non write back instructions where Rn != PC, it doesn't matter because the values are the same. Regarding the second point above, it would possibly be prudent to add some checking to prep_emulate_ldr_str(), so that instructions with both write back and Rn == PC would be rejected. Signed-off-by: Viktor Rosendahl <viktor.rosendahl@nokia.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/kernel/kprobes-decode.c10
1 files changed, 6 insertions, 4 deletions
diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c
index 8f6ed43861f1..23891317dc4b 100644
--- a/arch/arm/kernel/kprobes-decode.c
+++ b/arch/arm/kernel/kprobes-decode.c
@@ -594,7 +594,8 @@ static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
594 long cpsr = regs->ARM_cpsr; 594 long cpsr = regs->ARM_cpsr;
595 595
596 fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn); 596 fnr.dr = insnslot_llret_3arg_rflags(rnv, 0, rmv, cpsr, i_fn);
597 regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */ 597 if (rn != 15)
598 regs->uregs[rn] = fnr.r0; /* Save Rn in case of writeback. */
598 rdv = fnr.r1; 599 rdv = fnr.r1;
599 600
600 if (rd == 15) { 601 if (rd == 15) {
@@ -622,10 +623,11 @@ static void __kprobes emulate_str(struct kprobe *p, struct pt_regs *regs)
622 long rdv = (rd == 15) ? iaddr + str_pc_offset : regs->uregs[rd]; 623 long rdv = (rd == 15) ? iaddr + str_pc_offset : regs->uregs[rd];
623 long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn]; 624 long rnv = (rn == 15) ? iaddr + 8 : regs->uregs[rn];
624 long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */ 625 long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
626 long rnv_wb;
625 627
626 /* Save Rn in case of writeback. */ 628 rnv_wb = insnslot_3arg_rflags(rnv, rdv, rmv, regs->ARM_cpsr, i_fn);
627 regs->uregs[rn] = 629 if (rn != 15)
628 insnslot_3arg_rflags(rnv, rdv, rmv, regs->ARM_cpsr, i_fn); 630 regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */
629} 631}
630 632
631static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs) 633static void __kprobes emulate_mrrc(struct kprobe *p, struct pt_regs *regs)