Thread (18 messages) flat view 18 messages, 4 authors, 2018-10-02

[PATCH v2 7/7] arm64: uprobes - ARM32 instruction probing

From: Julien Thierry <hidden>
Date: 2018-09-27 16:18:23
Also in: lkml

Hi Maciej,

On 26/09/18 13:12, Maciej Slodczyk wrote:
quoted hunk ↗ jump to hunk
Detect what kind of instruction is being probed and depending on the
result:
- if an A64 instruction handle it the old way, using existing A64
instructions probing code,
- if an A32 instruction decode it and handle using the new code, moved
from 32 bit arm kernel tree.

Signed-off-by: Maciej Slodczyk <redacted>
---
  arch/arm64/kernel/debug-monitors.c |   8 +++
  arch/arm64/kernel/probes/uprobes.c | 111 ++++++++++++++++++++++++++++++++++---
  lib/probes/arm/actions-arm.c       |  35 ++++++++----
  lib/probes/arm/decode-arm.c        |  16 +++++-
  4 files changed, 148 insertions(+), 22 deletions(-)
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index 06ca574..ee10c0b 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -366,6 +366,14 @@ int aarch32_break_handler(struct pt_regs *regs)
  	if (!bp)
  		return -EFAULT;
  
+	/*
+	 * Since bp != false, a sofware breakpoint instruction is being handled.
+	 * If in user mode (compat_user_mode() few lines above),
+	 * try to handle it by an uprobe handler, if registered.
+	 */
+	if (!brk_handler((unsigned long)pc, BRK64_ESR_UPROBES, regs))
+		return 0;
+
  	send_user_sigtrap(TRAP_BRKPT);
  	return 0;
  }
diff --git a/arch/arm64/kernel/probes/uprobes.c b/arch/arm64/kernel/probes/uprobes.c
index e7b8912..872c8ec 100644
--- a/arch/arm64/kernel/probes/uprobes.c
+++ b/arch/arm64/kernel/probes/uprobes.c
@@ -11,9 +11,49 @@
  #include <asm/cacheflush.h>
  
  #include "decode-insn.h"
+#include "decode.h"
+#include "decode-arm.h"
+#include <../../../arm/include/asm/opcodes.h>
I'm almost positive this is frowned upon. I don't see any headers from 
arch/arm every being included in arch/arm64.
quoted hunk ↗ jump to hunk
  
  #define UPROBE_INV_FAULT_CODE	UINT_MAX
  
+uprobe_opcode_t get_swbp_insn(void)
+{
+	if (is_compat_task())
+		return AARCH32_BREAK_ARM;
+	else
+		return UPROBE_SWBP_INSN;
+}
+
+bool is_swbp_insn(uprobe_opcode_t *insn)
+{
+	return ((__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
+				(AARCH32_BREAK_ARM & 0x0fffffff)) ||
+				*insn == UPROBE_SWBP_INSN;
+}
+
+int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
+	     unsigned long vaddr)
+{
+	if (auprobe->arch == UPROBE_AARCH32)
+		return uprobe_write_opcode(auprobe, mm, vaddr,
+				__opcode_to_mem_arm(auprobe->bp_insn));
+	else
+		return uprobe_write_opcode(auprobe, mm, vaddr,
+				UPROBE_SWBP_INSN);
+}
+
+int set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
+		unsigned long vaddr)
+{
+	if (auprobe->arch == UPROBE_AARCH32)
+		return uprobe_write_opcode(auprobe, mm, vaddr,
+				auprobe->orig_insn);
+	else
+		return uprobe_write_opcode(auprobe, mm, vaddr,
+				*(uprobe_opcode_t *)&auprobe->insn);
+}
+
  void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
  		void *src, unsigned long len)
  {
@@ -38,16 +78,44 @@ int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
  		unsigned long addr)
  {
  	probes_opcode_t insn;
+	enum probes_insn retval;
+	unsigned int bpinsn;
  
-	/* TODO: Currently we do not support AARCH32 instruction probing */
-	if (mm->context.flags & MMCF_AARCH32)
-		return -ENOTSUPP;
-	else if (!IS_ALIGNED(addr, AARCH64_INSN_SIZE))
+	insn = *(probes_opcode_t *)(&auprobe->insn[0]);
+
+	if (!IS_ALIGNED(addr, AARCH64_INSN_SIZE))
  		return -EINVAL;
  
-	insn = *(probes_opcode_t *)(&auprobe->insn[0]);
+	/* check if AARCH32 */
+	if (is_compat_task()) {
+
+		/* Thumb is not supported yet */
+		if (addr & 0x3)
+			return -EINVAL;
-ENOTSUPP?

Cheers,

Julien
quoted hunk ↗ jump to hunk
+
+		retval = arm_probes_decode_insn(insn, &auprobe->api, false,
+					     uprobes_probes_actions, NULL);
+		auprobe->arch = UPROBE_AARCH32;
+
+		/*
+		 * original instruction could have been modified
+		 * when preparing for xol on AArch32
+		 */
+		auprobe->orig_insn = insn;
+
+		bpinsn = AARCH32_BREAK_ARM & 0x0fffffff;
+		if (insn >= 0xe0000000) /* Unconditional instruction */
+			bpinsn |= 0xe0000000;
+		else /* Copy condition from insn */
+			bpinsn |= insn & 0xf0000000;
+
+		auprobe->bp_insn = bpinsn;
+	} else {
+		retval = arm64_probes_decode_insn(insn, &auprobe->api);
+		auprobe->arch = UPROBE_AARCH64;
+	}
  
-	switch (arm64_probes_decode_insn(insn, &auprobe->api)) {
+	switch (retval) {
  	case INSN_REJECTED:
  		return -EINVAL;
  
@@ -66,6 +134,9 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
  {
  	struct uprobe_task *utask = current->utask;
  
+	if (auprobe->prehandler)
+		auprobe->prehandler(auprobe, &utask->autask, regs);
+
  	/* Initialize with an invalid fault code to detect if ol insn trapped */
  	current->thread.fault_code = UPROBE_INV_FAULT_CODE;
  
@@ -88,6 +159,9 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
  
  	user_disable_single_step(current);
  
+	if (auprobe->posthandler)
+		auprobe->posthandler(auprobe, &utask->autask, regs);
+
  	return 0;
  }
  bool arch_uprobe_xol_was_trapped(struct task_struct *t)
@@ -103,10 +177,24 @@ bool arch_uprobe_xol_was_trapped(struct task_struct *t)
  	return false;
  }
  
+bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+	probes_opcode_t insn = *(probes_opcode_t *)(&auprobe->insn[0]);
+	pstate_check_t *check = (*aarch32_opcode_cond_checks[insn >> 28]);
+
+	if (auprobe->arch == UPROBE_AARCH64)
+		return false;
+
+	if (!check(regs->pstate & 0xffffffff)) {
+		instruction_pointer_set(regs, instruction_pointer(regs) + 4);
+		return true;
+	}
+	return false;
+}
+
  bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
  {
  	probes_opcode_t insn;
-	unsigned long addr;
  
  	if (!auprobe->simulate)
  		return false;
@@ -154,9 +242,14 @@ arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
  {
  	unsigned long orig_ret_vaddr;
  
-	orig_ret_vaddr = procedure_link_pointer(regs);
  	/* Replace the return addr with trampoline addr */
-	procedure_link_pointer_set(regs, trampoline_vaddr);
+	if (is_compat_task()) {
+		orig_ret_vaddr = link_register(regs);
+		link_register_set(regs, trampoline_vaddr);
+	} else {
+		orig_ret_vaddr = procedure_link_pointer(regs);
+		procedure_link_pointer_set(regs, trampoline_vaddr);
+	}
  
  	return orig_ret_vaddr;
  }
diff --git a/lib/probes/arm/actions-arm.c b/lib/probes/arm/actions-arm.c
index 2393573..cee1496 100644
--- a/lib/probes/arm/actions-arm.c
+++ b/lib/probes/arm/actions-arm.c
@@ -15,10 +15,7 @@
  
  #include "decode.h"
  #include "decode-arm.h"
-
-#ifdef CONFIG_ARM64
  #include <../../../arm/include/asm/opcodes.h>
-#endif /* CONFIG_ARM64  */
  
  static int uprobes_substitute_pc(unsigned long *pinsn, u32 oregs)
  {
@@ -75,8 +72,13 @@ static void uprobe_set_pc(struct arch_uprobe *auprobe,
  {
  	u32 pcreg = auprobe->pcreg;
  
-	autask->backup = pt_regs_read_reg(regs, pcreg);
-	pt_regs_write_reg(regs, pcreg, instruction_pointer(regs) + 8);
+	if (pcreg == 15) {
+		autask->backup = instruction_pointer(regs);
+		instruction_pointer_set(regs, instruction_pointer(regs) + 8);
+	} else {
+		autask->backup = pt_regs_read_reg(regs, pcreg);
+		pt_regs_write_reg(regs, pcreg, instruction_pointer(regs) + 8);
+	}
  }
  
  static void uprobe_unset_pc(struct arch_uprobe *auprobe,
@@ -84,7 +86,10 @@ static void uprobe_unset_pc(struct arch_uprobe *auprobe,
  			    struct pt_regs *regs)
  {
  	/* PC will be taken care of by common code */
-	pt_regs_write_reg(regs, auprobe->pcreg, autask->backup);
+	if (auprobe->pcreg == 15)
+		instruction_pointer_set(regs, autask->backup);
+	else
+		pt_regs_write_reg(regs, auprobe->pcreg, autask->backup);
  }
  
  static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
@@ -93,8 +98,13 @@ static void uprobe_aluwrite_pc(struct arch_uprobe *auprobe,
  {
  	u32 pcreg = auprobe->pcreg;
  
-	alu_write_pc(pt_regs_read_reg(regs, pcreg), regs);
-	pt_regs_write_reg(regs, pcreg, autask->backup);
+	if (pcreg == 15) {
+		alu_write_pc(instruction_pointer(regs), regs);
+		instruction_pointer_set(regs, autask->backup);
+	} else {
+		alu_write_pc(pt_regs_read_reg(regs, pcreg), regs);
+		pt_regs_write_reg(regs, pcreg, autask->backup);
+	}
  }
  
  static void uprobe_write_pc(struct arch_uprobe *auprobe,
@@ -103,8 +113,13 @@ static void uprobe_write_pc(struct arch_uprobe *auprobe,
  {
  	u32 pcreg = auprobe->pcreg;
  
-	load_write_pc(pt_regs_read_reg(regs, pcreg), regs);
-	pt_regs_write_reg(regs, pcreg, autask->backup);
+	if (pcreg == 15) {
+		load_write_pc(instruction_pointer(regs), regs);
+		instruction_pointer_set(regs, autask->backup);
+	} else {
+		load_write_pc(pt_regs_read_reg(regs, pcreg), regs);
+		pt_regs_write_reg(regs, pcreg, autask->backup);
+	}
  }
  
  enum probes_insn
diff --git a/lib/probes/arm/decode-arm.c b/lib/probes/arm/decode-arm.c
index 36eb939..2f2a810 100644
--- a/lib/probes/arm/decode-arm.c
+++ b/lib/probes/arm/decode-arm.c
@@ -87,11 +87,18 @@ void __kprobes simulate_blx2bx(probes_opcode_t insn,
  		struct arch_probes_insn *asi, struct pt_regs *regs)
  {
  	int rm = insn & 0xf;
-	long rmv = pt_regs_read_reg(regs, rm);
+	long rmv;
  	long cpsr;
  
+	if (rm == 15)
+		rmv = (long) instruction_pointer(regs);
+	else
+		rmv = pt_regs_read_reg(regs, rm);
+
  	if (insn & (1 << 5))
-		link_register_set(regs, (long) instruction_pointer(regs));
+		link_register_set(regs,
+				(long) instruction_pointer(regs)
+					+ ARM_COMPAT_LR_OFFSET);
  
  	instruction_pointer_set(regs, rmv & ~0x1);
  	cpsr = state_register(regs) & ~PSR_T_BIT;
@@ -108,7 +115,10 @@ void __kprobes simulate_mrs(probes_opcode_t insn,
  	int rd = (insn >> 12) & 0xf;
  	unsigned long mask = 0xf8ff03df; /* Mask out execution state */
  
-	pt_regs_write_reg(regs, rd, state_register(regs) & mask);
+	if (rd == 15)
+		instruction_pointer_set(regs, state_register(regs) & mask);
+	else
+		pt_regs_write_reg(regs, rd, state_register(regs) & mask);
  }
  
  void __kprobes simulate_mov_ipsp(probes_opcode_t insn,
-- 
Julien Thierry
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help