Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad111ce4 authored by Jon Medhurst's avatar Jon Medhurst Committed by Nicolas Pitre
Browse files

ARM: kprobes: Fix emulation of CMP, CMN, TST and TEQ instructions.



Probing these instructions was corrupting R0 because the emulation code
didn't account for the fact that they don't write a result to a
register.

Signed-off-by: default avatarJon Medhurst <tixy@yxit.co.uk>
Signed-off-by: default avatarNicolas Pitre <nicolas.pitre@linaro.org>
parent a539f5d4
Loading
Loading
Loading
Loading
+53 −2
Original line number Original line Diff line number Diff line
@@ -807,6 +807,17 @@ emulate_alu_imm_rwflags(struct kprobe *p, struct pt_regs *regs)
	regs->uregs[rd] = insnslot_1arg_rwflags(rnv, &regs->ARM_cpsr, i_fn);
	regs->uregs[rd] = insnslot_1arg_rwflags(rnv, &regs->ARM_cpsr, i_fn);
}
}


static void __kprobes
emulate_alu_tests_imm(struct kprobe *p, struct pt_regs *regs)
{
	insn_1arg_fn_t *i_fn = (insn_1arg_fn_t *)&p->ainsn.insn[0];
	kprobe_opcode_t insn = p->opcode;
	int rn = (insn >> 16) & 0xf;
	long rnv = (rn == 15) ? (long)p->addr + 8 : regs->uregs[rn];

	insnslot_1arg_rwflags(rnv, &regs->ARM_cpsr, i_fn);
}

static void __kprobes
static void __kprobes
emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs)
emulate_alu_rflags(struct kprobe *p, struct pt_regs *regs)
{
{
@@ -843,6 +854,22 @@ emulate_alu_rwflags(struct kprobe *p, struct pt_regs *regs)
		insnslot_3arg_rwflags(rnv, rmv, rsv, &regs->ARM_cpsr, i_fn);
		insnslot_3arg_rwflags(rnv, rmv, rsv, &regs->ARM_cpsr, i_fn);
}
}


static void __kprobes
emulate_alu_tests(struct kprobe *p, struct pt_regs *regs)
{
	insn_3arg_fn_t *i_fn = (insn_3arg_fn_t *)&p->ainsn.insn[0];
	kprobe_opcode_t insn = p->opcode;
	long ppc = (long)p->addr + 8;
	int rn = (insn >> 16) & 0xf;
	int rs = (insn >> 8) & 0xf;	/* rs/rsv may be invalid, don't care. */
	int rm = insn & 0xf;
	long rnv = (rn == 15) ? ppc : regs->uregs[rn];
	long rmv = (rm == 15) ? ppc : regs->uregs[rm];
	long rsv = regs->uregs[rs];

	insnslot_3arg_rwflags(rnv, rmv, rsv, &regs->ARM_cpsr, i_fn);
}

static enum kprobe_insn __kprobes
static enum kprobe_insn __kprobes
prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi)
prep_emulate_ldr_str(kprobe_opcode_t insn, struct arch_specific_insn *asi)
{
{
@@ -1142,8 +1169,20 @@ space_cccc_000x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
		insn |= 0x00000200;     /* Rs = r2 */
		insn |= 0x00000200;     /* Rs = r2 */
	}
	}
	asi->insn[0] = insn;
	asi->insn[0] = insn;

	if ((insn & 0x0f900000) == 0x01100000) {
		/*
		 * TST : cccc 0001 0001 xxxx xxxx xxxx xxxx xxxx
		 * TEQ : cccc 0001 0011 xxxx xxxx xxxx xxxx xxxx
		 * CMP : cccc 0001 0101 xxxx xxxx xxxx xxxx xxxx
		 * CMN : cccc 0001 0111 xxxx xxxx xxxx xxxx xxxx
		 */
		asi->insn_handler = emulate_alu_tests;
	} else {
		/* ALU ops which write to Rd */
		asi->insn_handler = (insn & (1 << 20)) ?  /* S-bit */
		asi->insn_handler = (insn & (1 << 20)) ?  /* S-bit */
				emulate_alu_rwflags : emulate_alu_rflags;
				emulate_alu_rwflags : emulate_alu_rflags;
	}
	return INSN_GOOD;
	return INSN_GOOD;
}
}


@@ -1170,8 +1209,20 @@ space_cccc_001x(kprobe_opcode_t insn, struct arch_specific_insn *asi)
	 */
	 */
	insn &= 0xffff0fff;	/* Rd = r0 */
	insn &= 0xffff0fff;	/* Rd = r0 */
	asi->insn[0] = insn;
	asi->insn[0] = insn;

	if ((insn & 0x0f900000) == 0x03100000) {
		/*
		 * TST : cccc 0011 0001 xxxx xxxx xxxx xxxx xxxx
		 * TEQ : cccc 0011 0011 xxxx xxxx xxxx xxxx xxxx
		 * CMP : cccc 0011 0101 xxxx xxxx xxxx xxxx xxxx
		 * CMN : cccc 0011 0111 xxxx xxxx xxxx xxxx xxxx
		 */
		asi->insn_handler = emulate_alu_tests_imm;
	} else {
		/* ALU ops which write to Rd */
		asi->insn_handler = (insn & (1 << 20)) ?  /* S-bit */
		asi->insn_handler = (insn & (1 << 20)) ?  /* S-bit */
			emulate_alu_imm_rwflags : emulate_alu_imm_rflags;
			emulate_alu_imm_rwflags : emulate_alu_imm_rflags;
	}
	return INSN_GOOD;
	return INSN_GOOD;
}
}