aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNaveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>2017-06-01 06:48:17 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2017-06-16 05:49:43 -0400
commitc05b8c4474c03026aaa7f8872e78369f69f1bb08 (patch)
tree0dec69ce1815755aec7d8cd7f4c1cdd33226b37f
parenta4979a7e71eb8da976cbe4a0a1fa50636e76b04f (diff)
powerpc/kprobes: Skip livepatch_handler() for jprobes
ftrace_caller() depends on a modified regs->nip to detect if a certain function has been livepatched. However, with KPROBES_ON_FTRACE, it is possible for regs->nip to have been modified by the kprobes pre_handler (jprobes, for instance). In this case, we do not want to invoke the livepatch_handler so as not to consume the livepatch stack. To distinguish between the two (kprobes and livepatch), we check if there is an active kprobe on the current function. If there is, then we know for sure that it must have modified the NIP as we don't support livepatching a kprobe'd function. In this case, we simply skip the livepatch_handler and branch to the new NIP. Otherwise, the livepatch_handler is invoked. Fixes: ead514d5fb30 ("powerpc/kprobes: Add support for KPROBES_ON_FTRACE") Signed-off-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com> Reviewed-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/include/asm/kprobes.h1
-rw-r--r--arch/powerpc/kernel/kprobes.c6
-rw-r--r--arch/powerpc/kernel/trace/ftrace_64_mprofile.S39
3 files changed, 41 insertions, 5 deletions
diff --git a/arch/powerpc/include/asm/kprobes.h b/arch/powerpc/include/asm/kprobes.h
index a83821f33ea3..8814a7249ceb 100644
--- a/arch/powerpc/include/asm/kprobes.h
+++ b/arch/powerpc/include/asm/kprobes.h
@@ -103,6 +103,7 @@ extern int kprobe_exceptions_notify(struct notifier_block *self,
103extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr); 103extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr);
104extern int kprobe_handler(struct pt_regs *regs); 104extern int kprobe_handler(struct pt_regs *regs);
105extern int kprobe_post_handler(struct pt_regs *regs); 105extern int kprobe_post_handler(struct pt_regs *regs);
106extern int is_current_kprobe_addr(unsigned long addr);
106#ifdef CONFIG_KPROBES_ON_FTRACE 107#ifdef CONFIG_KPROBES_ON_FTRACE
107extern int skip_singlestep(struct kprobe *p, struct pt_regs *regs, 108extern int skip_singlestep(struct kprobe *p, struct pt_regs *regs,
108 struct kprobe_ctlblk *kcb); 109 struct kprobe_ctlblk *kcb);
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index 5075a4d6f1d7..01addfb0ed0a 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -43,6 +43,12 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
43 43
44struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; 44struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
45 45
46int is_current_kprobe_addr(unsigned long addr)
47{
48 struct kprobe *p = kprobe_running();
49 return (p && (unsigned long)p->addr == addr) ? 1 : 0;
50}
51
46bool arch_within_kprobe_blacklist(unsigned long addr) 52bool arch_within_kprobe_blacklist(unsigned long addr)
47{ 53{
48 return (addr >= (unsigned long)__kprobes_text_start && 54 return (addr >= (unsigned long)__kprobes_text_start &&
diff --git a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S
index fa0921410fa4..c98e90b4ea7b 100644
--- a/arch/powerpc/kernel/trace/ftrace_64_mprofile.S
+++ b/arch/powerpc/kernel/trace/ftrace_64_mprofile.S
@@ -99,13 +99,39 @@ ftrace_call:
99 bl ftrace_stub 99 bl ftrace_stub
100 nop 100 nop
101 101
102 /* Load ctr with the possibly modified NIP */ 102 /* Load the possibly modified NIP */
103 ld r3, _NIP(r1) 103 ld r15, _NIP(r1)
104 mtctr r3 104
105#ifdef CONFIG_LIVEPATCH 105#ifdef CONFIG_LIVEPATCH
106 cmpd r14,r3 /* has NIP been altered? */ 106 cmpd r14, r15 /* has NIP been altered? */
107#endif 107#endif
108 108
109#if defined(CONFIG_LIVEPATCH) && defined(CONFIG_KPROBES_ON_FTRACE)
110 /* NIP has not been altered, skip over further checks */
111 beq 1f
112
113 /* Check if there is an active kprobe on us */
114 subi r3, r14, 4
115 bl is_current_kprobe_addr
116 nop
117
118 /*
119 * If r3 == 1, then this is a kprobe/jprobe.
120 * else, this is livepatched function.
121 *
122 * The conditional branch for livepatch_handler below will use the
123 * result of this comparison. For kprobe/jprobe, we just need to branch to
124 * the new NIP, not call livepatch_handler. The branch below is bne, so we
125 * want CR0[EQ] to be true if this is a kprobe/jprobe. Which means we want
126 * CR0[EQ] = (r3 == 1).
127 */
128 cmpdi r3, 1
1291:
130#endif
131
132 /* Load CTR with the possibly modified NIP */
133 mtctr r15
134
109 /* Restore gprs */ 135 /* Restore gprs */
110 REST_GPR(0,r1) 136 REST_GPR(0,r1)
111 REST_10GPRS(2,r1) 137 REST_10GPRS(2,r1)
@@ -123,7 +149,10 @@ ftrace_call:
123 addi r1, r1, SWITCH_FRAME_SIZE 149 addi r1, r1, SWITCH_FRAME_SIZE
124 150
125#ifdef CONFIG_LIVEPATCH 151#ifdef CONFIG_LIVEPATCH
126 /* Based on the cmpd above, if the NIP was altered handle livepatch */ 152 /*
153 * Based on the cmpd or cmpdi above, if the NIP was altered and we're
154 * not on a kprobe/jprobe, then handle livepatch.
155 */
127 bne- livepatch_handler 156 bne- livepatch_handler
128#endif 157#endif
129 158