Skip to content

Commit 39a67d4

Browse files
sandeepspctmarinas
authored andcommitted
arm64: kprobes instruction simulation support
Kprobes needs simulation of instructions that cannot be stepped from a different memory location, e.g.: those instructions that uses PC-relative addressing. In simulation, the behaviour of the instruction is implemented using a copy of pt_regs. The following instruction categories are simulated: - All branching instructions(conditional, register, and immediate) - Literal access instructions(load-literal, adr/adrp) Conditional execution is limited to branching instructions in ARM v8. If conditions at PSTATE do not match the condition fields of opcode, the instruction is effectively NOP. Thanks to Will Cohen for assorted suggested changes. Signed-off-by: Sandeepa Prabhu <sandeepa.s.prabhu@gmail.com> Signed-off-by: William Cohen <wcohen@redhat.com> Signed-off-by: David A. Long <dave.long@linaro.org> Acked-by: Masami Hiramatsu <mhiramat@kernel.org> [catalin.marinas@arm.com: removed linux/module.h include] Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
1 parent 888b3c8 commit 39a67d4

File tree

8 files changed

+326
-15
lines changed

8 files changed

+326
-15
lines changed

arch/arm64/include/asm/probes.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@
1515
#ifndef _ARM_PROBES_H
1616
#define _ARM_PROBES_H
1717

18+
#include <asm/opcodes.h>
19+
1820
struct kprobe;
1921
struct arch_specific_insn;
2022

2123
typedef u32 kprobe_opcode_t;
22-
typedef unsigned long (kprobes_pstate_check_t)(unsigned long);
2324
typedef void (kprobes_handler_t) (u32 opcode, long addr, struct pt_regs *);
2425

2526
/* architecture specific copy of original instruction */
2627
struct arch_specific_insn {
2728
kprobe_opcode_t *insn;
28-
kprobes_pstate_check_t *pstate_cc;
29+
pstate_check_t *pstate_cc;
2930
kprobes_handler_t *handler;
3031
/* restore address after step xol */
3132
unsigned long restore;

arch/arm64/kernel/insn.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <asm/cacheflush.h>
3131
#include <asm/debug-monitors.h>
3232
#include <asm/fixmap.h>
33+
#include <asm/opcodes.h>
3334
#include <asm/insn.h>
3435

3536
#define AARCH64_INSN_SF_BIT BIT(31)

arch/arm64/kernel/probes/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o
1+
obj-$(CONFIG_KPROBES) += kprobes.o decode-insn.o \
2+
simulate-insn.o

arch/arm64/kernel/probes/decode-insn.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <asm/sections.h>
2222

2323
#include "decode-insn.h"
24+
#include "simulate-insn.h"
2425

2526
static bool __kprobes aarch64_insn_is_steppable(u32 insn)
2627
{
@@ -74,6 +75,7 @@ static bool __kprobes aarch64_insn_is_steppable(u32 insn)
7475
/* Return:
7576
* INSN_REJECTED If instruction is one not allowed to kprobe,
7677
* INSN_GOOD If instruction is supported and uses instruction slot,
78+
* INSN_GOOD_NO_SLOT If instruction is supported but doesn't use its slot.
7779
*/
7880
static enum kprobe_insn __kprobes
7981
arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
@@ -84,8 +86,37 @@ arm_probe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi)
8486
*/
8587
if (aarch64_insn_is_steppable(insn))
8688
return INSN_GOOD;
87-
else
89+
90+
if (aarch64_insn_is_bcond(insn)) {
91+
asi->handler = simulate_b_cond;
92+
} else if (aarch64_insn_is_cbz(insn) ||
93+
aarch64_insn_is_cbnz(insn)) {
94+
asi->handler = simulate_cbz_cbnz;
95+
} else if (aarch64_insn_is_tbz(insn) ||
96+
aarch64_insn_is_tbnz(insn)) {
97+
asi->handler = simulate_tbz_tbnz;
98+
} else if (aarch64_insn_is_adr_adrp(insn)) {
99+
asi->handler = simulate_adr_adrp;
100+
} else if (aarch64_insn_is_b(insn) ||
101+
aarch64_insn_is_bl(insn)) {
102+
asi->handler = simulate_b_bl;
103+
} else if (aarch64_insn_is_br(insn) ||
104+
aarch64_insn_is_blr(insn) ||
105+
aarch64_insn_is_ret(insn)) {
106+
asi->handler = simulate_br_blr_ret;
107+
} else if (aarch64_insn_is_ldr_lit(insn)) {
108+
asi->handler = simulate_ldr_literal;
109+
} else if (aarch64_insn_is_ldrsw_lit(insn)) {
110+
asi->handler = simulate_ldrsw_literal;
111+
} else {
112+
/*
113+
* Instruction cannot be stepped out-of-line and we don't
114+
* (yet) simulate it.
115+
*/
88116
return INSN_REJECTED;
117+
}
118+
119+
return INSN_GOOD_NO_SLOT;
89120
}
90121

91122
static bool __kprobes

arch/arm64/kernel/probes/decode-insn.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
enum kprobe_insn {
2727
INSN_REJECTED,
28+
INSN_GOOD_NO_SLOT,
2829
INSN_GOOD,
2930
};
3031

arch/arm64/kernel/probes/kprobes.c

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ void jprobe_return_break(void);
4545
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
4646
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
4747

48+
static void __kprobes
49+
post_kprobe_handler(struct kprobe_ctlblk *, struct pt_regs *);
50+
4851
static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
4952
{
5053
/* prepare insn slot */
@@ -61,6 +64,23 @@ static void __kprobes arch_prepare_ss_slot(struct kprobe *p)
6164
sizeof(kprobe_opcode_t);
6265
}
6366

67+
static void __kprobes arch_prepare_simulate(struct kprobe *p)
68+
{
69+
/* This instructions is not executed xol. No need to adjust the PC */
70+
p->ainsn.restore = 0;
71+
}
72+
73+
static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
74+
{
75+
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
76+
77+
if (p->ainsn.handler)
78+
p->ainsn.handler((u32)p->opcode, (long)p->addr, regs);
79+
80+
/* single step simulated, now go for post processing */
81+
post_kprobe_handler(kcb, regs);
82+
}
83+
6484
int __kprobes arch_prepare_kprobe(struct kprobe *p)
6585
{
6686
unsigned long probe_addr = (unsigned long)p->addr;
@@ -84,6 +104,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
84104
case INSN_REJECTED: /* insn not supported */
85105
return -EINVAL;
86106

107+
case INSN_GOOD_NO_SLOT: /* insn need simulation */
108+
p->ainsn.insn = NULL;
109+
break;
110+
87111
case INSN_GOOD: /* instruction uses slot */
88112
p->ainsn.insn = get_insn_slot();
89113
if (!p->ainsn.insn)
@@ -92,7 +116,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
92116
};
93117

94118
/* prepare the instruction */
95-
arch_prepare_ss_slot(p);
119+
if (p->ainsn.insn)
120+
arch_prepare_ss_slot(p);
121+
else
122+
arch_prepare_simulate(p);
96123

97124
return 0;
98125
}
@@ -218,20 +245,24 @@ static void __kprobes setup_singlestep(struct kprobe *p,
218245
kcb->kprobe_status = KPROBE_HIT_SS;
219246
}
220247

221-
BUG_ON(!p->ainsn.insn);
222248

223-
/* prepare for single stepping */
224-
slot = (unsigned long)p->ainsn.insn;
249+
if (p->ainsn.insn) {
250+
/* prepare for single stepping */
251+
slot = (unsigned long)p->ainsn.insn;
225252

226-
set_ss_context(kcb, slot); /* mark pending ss */
253+
set_ss_context(kcb, slot); /* mark pending ss */
227254

228-
if (kcb->kprobe_status == KPROBE_REENTER)
229-
spsr_set_debug_flag(regs, 0);
255+
if (kcb->kprobe_status == KPROBE_REENTER)
256+
spsr_set_debug_flag(regs, 0);
230257

231-
/* IRQs and single stepping do not mix well. */
232-
kprobes_save_local_irqflag(kcb, regs);
233-
kernel_enable_single_step(regs);
234-
instruction_pointer_set(regs, slot);
258+
/* IRQs and single stepping do not mix well. */
259+
kprobes_save_local_irqflag(kcb, regs);
260+
kernel_enable_single_step(regs);
261+
instruction_pointer_set(regs, slot);
262+
} else {
263+
/* insn simulation */
264+
arch_simulate_insn(p, regs);
265+
}
235266
}
236267

237268
static int __kprobes reenter_kprobe(struct kprobe *p,

0 commit comments

Comments
 (0)