diff options
| author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2012-09-05 23:31:25 +0900 | 
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2012-09-13 22:52:11 -0400 | 
| commit | c6aaf4d0bb86e2154ea31a33804cec300611255f (patch) | |
| tree | 34f973a7ee081daa773b8d4cea9cffaf28bf018a | |
| parent | 47d5a5f88b9d25d6464c9b60c28f391e84e3ed65 (diff) | |
| download | olio-linux-3.10-c6aaf4d0bb86e2154ea31a33804cec300611255f.tar.xz olio-linux-3.10-c6aaf4d0bb86e2154ea31a33804cec300611255f.zip | |
kprobes/x86: Fix to support jprobes on ftrace-based kprobe
Fix kprobes/x86 to support jprobes on ftrace-based kprobes.
Because of -mfentry support of ftrace, ftrace is now put
on the beginning of function where jprobes are put.
Originally ftrace-based kprobes doesn't support jprobe
because it will change regs->ip and ftrace doesn't support
changing IP and ftrace itself doesn't conflict jprobe.
However, ftrace -mfentry support moves mcount call on the
top of functions where jprobes are put. This means that
jprobe always conflicts with ftrace-based kprobe and fails.
This patch allows ftrace-based kprobes to support jprobes
by allowing to modify regs->ip and kprobes breakpoint
handler also allows to skip singlestepping because there
is a ftrace call (not an original instruction).
Link: http://lkml.kernel.org/r/20120905143125.10329.90836.stgit@localhost.localdomain
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
| -rw-r--r-- | arch/x86/kernel/kprobes.c | 42 | ||||
| -rw-r--r-- | kernel/kprobes.c | 3 | 
2 files changed, 29 insertions, 16 deletions
| diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c index f49f60cca40..b7c2a85d192 100644 --- a/arch/x86/kernel/kprobes.c +++ b/arch/x86/kernel/kprobes.c @@ -541,6 +541,8 @@ reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb  	return 1;  } +static void __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs, +				      struct kprobe_ctlblk *kcb);  /*   * Interrupts are disabled on entry as trap3 is an interrupt gate and they   * remain disabled throughout this function. @@ -599,6 +601,12 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)  	} else if (kprobe_running()) {  		p = __this_cpu_read(current_kprobe);  		if (p->break_handler && p->break_handler(p, regs)) { +#ifdef KPROBES_CAN_USE_FTRACE +			if (kprobe_ftrace(p)) { +				skip_singlestep(p, regs, kcb); +				return 1; +			} +#endif  			setup_singlestep(p, regs, kcb, 0);  			return 1;  		} @@ -1053,6 +1061,21 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)  }  #ifdef KPROBES_CAN_USE_FTRACE +static void __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs, +				      struct kprobe_ctlblk *kcb) +{ +	/* +	 * Emulate singlestep (and also recover regs->ip) +	 * as if there is a 5byte nop +	 */ +	regs->ip = (unsigned long)p->addr + MCOUNT_INSN_SIZE; +	if (unlikely(p->post_handler)) { +		kcb->kprobe_status = KPROBE_HIT_SSDONE; +		p->post_handler(p, regs, 0); +	} +	__this_cpu_write(current_kprobe, NULL); +} +  /* Ftrace callback handler for kprobes */  void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,  				     struct ftrace_ops *ops, struct pt_regs *regs) @@ -1077,19 +1100,12 @@ void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,  		__this_cpu_write(current_kprobe, p);  		kcb->kprobe_status = KPROBE_HIT_ACTIVE; -		if (p->pre_handler) -			p->pre_handler(p, regs); - -		if (unlikely(p->post_handler)) { -			/* -			 * Emulate singlestep (and also recover regs->ip) -			 * as if there is a 5byte nop -			 */ -			regs->ip = ip + MCOUNT_INSN_SIZE; -			kcb->kprobe_status = KPROBE_HIT_SSDONE; -			p->post_handler(p, regs, 0); -		} -		__this_cpu_write(current_kprobe, NULL); +		if (!p->pre_handler || !p->pre_handler(p, regs)) +			skip_singlestep(p, regs, kcb); +		/* +		 * If pre_handler returns !0, it sets regs->ip and +		 * resets current kprobe. +		 */  	}  end:  	local_irq_restore(flags); diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 35b4315d84f..098f396aa40 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1418,9 +1418,6 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p,  		/* Given address is not on the instruction boundary */  		if ((unsigned long)p->addr != ftrace_addr)  			return -EILSEQ; -		/* break_handler (jprobe) can not work with ftrace */ -		if (p->break_handler) -			return -EINVAL;  		p->flags |= KPROBE_FLAG_FTRACE;  #else	/* !KPROBES_CAN_USE_FTRACE */  		return -EINVAL; |