Commit f315decb authored by Abhishek Sagar's avatar Abhishek Sagar Committed by Ingo Molnar

x86: kprobes change kprobe_handler flow

Signed-off-by: default avatarAbhishek Sagar <sagar.abhishek@gmail.com>
Signed-off-by: default avatarQuentin Barnes <qbarnes@gmail.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 4c491562
...@@ -442,6 +442,34 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, ...@@ -442,6 +442,34 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
/* Replace the return addr with trampoline addr */ /* Replace the return addr with trampoline addr */
*sara = (unsigned long) &kretprobe_trampoline; *sara = (unsigned long) &kretprobe_trampoline;
} }
static void __kprobes recursive_singlestep(struct kprobe *p,
struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{
save_previous_kprobe(kcb);
set_current_kprobe(p, regs, kcb);
kprobes_inc_nmissed_count(p);
prepare_singlestep(p, regs);
kcb->kprobe_status = KPROBE_REENTER;
}
static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb)
{
#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
if (p->ainsn.boostable == 1 && !p->post_handler) {
/* Boost up -- we can execute copied instructions directly */
reset_current_kprobe();
regs->ip = (unsigned long)p->ainsn.insn;
preempt_enable_no_resched();
return;
}
#endif
prepare_singlestep(p, regs);
kcb->kprobe_status = KPROBE_HIT_SS;
}
/* /*
* We have reentered the kprobe_handler(), since another probe was hit while * We have reentered the kprobe_handler(), since another probe was hit while
* within the handler. We save the original kprobes variables and just single * within the handler. We save the original kprobes variables and just single
...@@ -450,13 +478,9 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, ...@@ -450,13 +478,9 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb) struct kprobe_ctlblk *kcb)
{ {
if (kcb->kprobe_status == KPROBE_HIT_SS && switch (kcb->kprobe_status) {
*p->ainsn.insn == BREAKPOINT_INSTRUCTION) { case KPROBE_HIT_SSDONE:
regs->flags &= ~X86_EFLAGS_TF;
regs->flags |= kcb->kprobe_saved_flags;
return 0;
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
} else if (kcb->kprobe_status == KPROBE_HIT_SSDONE) {
/* TODO: Provide re-entrancy from post_kprobes_handler() and /* TODO: Provide re-entrancy from post_kprobes_handler() and
* avoid exception stack corruption while single-stepping on * avoid exception stack corruption while single-stepping on
* the instruction of the new probe. * the instruction of the new probe.
...@@ -464,14 +488,26 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, ...@@ -464,14 +488,26 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
arch_disarm_kprobe(p); arch_disarm_kprobe(p);
regs->ip = (unsigned long)p->addr; regs->ip = (unsigned long)p->addr;
reset_current_kprobe(); reset_current_kprobe();
return 1; preempt_enable_no_resched();
break;
#endif #endif
case KPROBE_HIT_ACTIVE:
recursive_singlestep(p, regs, kcb);
break;
case KPROBE_HIT_SS:
if (*p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
regs->flags &= ~TF_MASK;
regs->flags |= kcb->kprobe_saved_flags;
return 0;
} else {
recursive_singlestep(p, regs, kcb);
}
break;
default:
/* impossible cases */
WARN_ON(1);
} }
save_previous_kprobe(kcb);
set_current_kprobe(p, regs, kcb);
kprobes_inc_nmissed_count(p);
prepare_singlestep(p, regs);
kcb->kprobe_status = KPROBE_REENTER;
return 1; return 1;
} }
...@@ -481,83 +517,66 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs, ...@@ -481,83 +517,66 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
*/ */
static int __kprobes kprobe_handler(struct pt_regs *regs) static int __kprobes kprobe_handler(struct pt_regs *regs)
{ {
struct kprobe *p;
int ret = 0;
kprobe_opcode_t *addr; kprobe_opcode_t *addr;
struct kprobe *p;
struct kprobe_ctlblk *kcb; struct kprobe_ctlblk *kcb;
addr = (kprobe_opcode_t *)(regs->ip - sizeof(kprobe_opcode_t)); addr = (kprobe_opcode_t *)(regs->ip - sizeof(kprobe_opcode_t));
if (*addr != BREAKPOINT_INSTRUCTION) {
/*
* The breakpoint instruction was removed right
* after we hit it. Another cpu has removed
* either a probepoint or a debugger breakpoint
* at this address. In either case, no further
* handling of this interrupt is appropriate.
* Back up over the (now missing) int3 and run
* the original instruction.
*/
regs->ip = (unsigned long)addr;
return 1;
}
/* /*
* We don't want to be preempted for the entire * We don't want to be preempted for the entire
* duration of kprobe processing * duration of kprobe processing. We conditionally
* re-enable preemption at the end of this function,
* and also in reenter_kprobe() and setup_singlestep().
*/ */
preempt_disable(); preempt_disable();
kcb = get_kprobe_ctlblk();
kcb = get_kprobe_ctlblk();
p = get_kprobe(addr); p = get_kprobe(addr);
if (p) { if (p) {
/* Check we're not actually recursing */
if (kprobe_running()) { if (kprobe_running()) {
ret = reenter_kprobe(p, regs, kcb); if (reenter_kprobe(p, regs, kcb))
if (kcb->kprobe_status == KPROBE_REENTER) return 1;
{
ret = 1;
goto out;
}
goto preempt_out;
} else { } else {
set_current_kprobe(p, regs, kcb); set_current_kprobe(p, regs, kcb);
kcb->kprobe_status = KPROBE_HIT_ACTIVE; kcb->kprobe_status = KPROBE_HIT_ACTIVE;
if (p->pre_handler && p->pre_handler(p, regs))
{
/* handler set things up, skip ss setup */
ret = 1;
goto out;
}
}
} else {
if (*addr != BREAKPOINT_INSTRUCTION) {
/* /*
* The breakpoint instruction was removed right * If we have no pre-handler or it returned 0, we
* after we hit it. Another cpu has removed * continue with normal processing. If we have a
* either a probepoint or a debugger breakpoint * pre-handler and it returned non-zero, it prepped
* at this address. In either case, no further * for calling the break_handler below on re-entry
* handling of this interrupt is appropriate. * for jprobe processing, so get out doing nothing
* Back up over the (now missing) int3 and run * more here.
* the original instruction.
*/ */
regs->ip = (unsigned long)addr; if (!p->pre_handler || !p->pre_handler(p, regs))
ret = 1; setup_singlestep(p, regs, kcb);
goto preempt_out; return 1;
} }
if (kprobe_running()) { } else if (kprobe_running()) {
p = __get_cpu_var(current_kprobe); p = __get_cpu_var(current_kprobe);
if (p->break_handler && p->break_handler(p, regs)) if (p->break_handler && p->break_handler(p, regs)) {
goto ss_probe; setup_singlestep(p, regs, kcb);
return 1;
} }
/* Not one of ours: let kernel handle it */ } /* else: not a kprobe fault; let the kernel handle it */
goto preempt_out;
}
ss_probe:
ret = 1;
#if !defined(CONFIG_PREEMPT) || defined(CONFIG_PM)
if (p->ainsn.boostable == 1 && !p->post_handler) {
/* Boost up -- we can execute copied instructions directly */
reset_current_kprobe();
regs->ip = (unsigned long)p->ainsn.insn;
goto preempt_out;
}
#endif
prepare_singlestep(p, regs);
kcb->kprobe_status = KPROBE_HIT_SS;
goto out;
preempt_out:
preempt_enable_no_resched(); preempt_enable_no_resched();
out: return 0;
return ret;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment