kprobes/arm: Fix ldrd/strd emulation
Currently emulate_ldrd and emulate_strd don't even have the adjustment of the PC value, so in case of Rn == PC, it will not update the PC incorrectly but instead load/store from the wrong address. Let's add both the adjustment of the PC value and the check for PC == PC. Signed-off-by: Viktor Rosendahl <viktor.rosendahl@nokia.com> Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
This commit is contained in:
parent
6221f222c0
commit
cf3cc1aa9b
|
@ -540,9 +540,12 @@ static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
|
|||
{
|
||||
insn_2arg_fn_t *i_fn = (insn_2arg_fn_t *)&p->ainsn.insn[0];
|
||||
kprobe_opcode_t insn = p->opcode;
|
||||
long ppc = (long)p->addr + 8;
|
||||
int rd = (insn >> 12) & 0xf;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int rm = insn & 0xf; /* rm may be invalid, don't care. */
|
||||
long rmv = (rm == 15) ? ppc : regs->uregs[rm];
|
||||
long rnv = (rn == 15) ? ppc : regs->uregs[rn];
|
||||
|
||||
/* Not following the C calling convention here, so need asm(). */
|
||||
__asm__ __volatile__ (
|
||||
|
@ -554,29 +557,36 @@ static void __kprobes emulate_ldrd(struct kprobe *p, struct pt_regs *regs)
|
|||
"str r0, %[rn] \n\t" /* in case of writeback */
|
||||
"str r2, %[rd0] \n\t"
|
||||
"str r3, %[rd1] \n\t"
|
||||
: [rn] "+m" (regs->uregs[rn]),
|
||||
: [rn] "+m" (rnv),
|
||||
[rd0] "=m" (regs->uregs[rd]),
|
||||
[rd1] "=m" (regs->uregs[rd+1])
|
||||
: [rm] "m" (regs->uregs[rm]),
|
||||
: [rm] "m" (rmv),
|
||||
[cpsr] "r" (regs->ARM_cpsr),
|
||||
[i_fn] "r" (i_fn)
|
||||
: "r0", "r1", "r2", "r3", "lr", "cc"
|
||||
);
|
||||
if (rn != 15)
|
||||
regs->uregs[rn] = rnv; /* Save Rn in case of writeback. */
|
||||
}
|
||||
|
||||
static void __kprobes emulate_strd(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
insn_4arg_fn_t *i_fn = (insn_4arg_fn_t *)&p->ainsn.insn[0];
|
||||
kprobe_opcode_t insn = p->opcode;
|
||||
long ppc = (long)p->addr + 8;
|
||||
int rd = (insn >> 12) & 0xf;
|
||||
int rn = (insn >> 16) & 0xf;
|
||||
int rm = insn & 0xf;
|
||||
long rnv = regs->uregs[rn];
|
||||
long rmv = regs->uregs[rm]; /* rm/rmv may be invalid, don't care. */
|
||||
long rnv = (rn == 15) ? ppc : regs->uregs[rn];
|
||||
/* rm/rmv may be invalid, don't care. */
|
||||
long rmv = (rm == 15) ? ppc : regs->uregs[rm];
|
||||
long rnv_wb;
|
||||
|
||||
regs->uregs[rn] = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
|
||||
rnv_wb = insnslot_4arg_rflags(rnv, rmv, regs->uregs[rd],
|
||||
regs->uregs[rd+1],
|
||||
regs->ARM_cpsr, i_fn);
|
||||
if (rn != 15)
|
||||
regs->uregs[rn] = rnv_wb; /* Save Rn in case of writeback. */
|
||||
}
|
||||
|
||||
static void __kprobes emulate_ldr(struct kprobe *p, struct pt_regs *regs)
|
||||
|
|
Loading…
Reference in New Issue