Skip to content

Commit

Permalink
bpf/error-inject/kprobes: Clear current_kprobe and enable preempt in …
Browse files Browse the repository at this point in the history
…kprobe

Clear current_kprobe and enable preemption in kprobe
even if pre_handler returns !0.

This simplifies function override using kprobes.

Jprobe used to require to keep the preemption disabled and
keep current_kprobe until it returned to original function
entry. For this reason kprobe_int3_handler() and similar
arch dependent kprobe handers checks pre_handler result
and exit without enabling preemption if the result is !0.

After removing the jprobe, Kprobes does not need to
keep preempt disabled even if user handler returns !0
anymore.

But since the function override handler in error-inject
and bpf is also returns !0 if it overrides a function,
to balancing the preempt count, it enables preemption
and reset current kprobe by itself.

That is a bad design that is very buggy. This fixes
such unbalanced preempt-count and current_kprobes setting
in kprobes, bpf and error-inject.

Note: for powerpc and x86, this removes all preempt_disable
from kprobe_ftrace_handler because ftrace callbacks are
called under preempt disabled.

Signed-off-by: Masami Hiramatsu <[email protected]>
Acked-by: Thomas Gleixner <[email protected]>
Acked-by: Naveen N. Rao <[email protected]>
Cc: Alexei Starovoitov <[email protected]>
Cc: Ananth N Mavinakayanahalli <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Catalin Marinas <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Fenghua Yu <[email protected]>
Cc: Heiko Carstens <[email protected]>
Cc: James Hogan <[email protected]>
Cc: Josef Bacik <[email protected]>
Cc: Linus Torvalds <[email protected]>
Cc: Martin Schwidefsky <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Ralf Baechle <[email protected]>
Cc: Rich Felker <[email protected]>
Cc: Russell King <[email protected]>
Cc: Steven Rostedt <[email protected]>
Cc: Tony Luck <[email protected]>
Cc: Vineet Gupta <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Yoshinori Sato <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Link: https://lore.kernel.org/lkml/152942494574.15209.12323837825873032258.stgit@devbox
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
mhiramat authored and Ingo Molnar committed Jun 21, 2018
1 parent d5ad85b commit cce188b
Show file tree
Hide file tree
Showing 14 changed files with 52 additions and 64 deletions.
5 changes: 3 additions & 2 deletions arch/arc/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs)
if (!p->pre_handler || !p->pre_handler(p, regs)) {
setup_singlestep(p, regs);
kcb->kprobe_status = KPROBE_HIT_SS;
} else {
reset_current_kprobe();
preempt_enable_no_resched();
}

return 1;
Expand Down Expand Up @@ -442,9 +445,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p,
kretprobe_assert(ri, orig_ret_address, trampoline_address);
regs->ret = orig_ret_address;

reset_current_kprobe();
kretprobe_hash_unlock(current, &flags);
preempt_enable_no_resched();

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist);
Expand Down
10 changes: 5 additions & 5 deletions arch/arm/probes/kprobes/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,10 +300,10 @@ void __kprobes kprobe_handler(struct pt_regs *regs)

/*
* If we have no pre-handler or it returned 0, we
* continue with normal processing. If we have a
* pre-handler and it returned non-zero, it prepped
* for calling the break_handler below on re-entry,
* so get out doing nothing more here.
* continue with normal processing. If we have a
* pre-handler and it returned non-zero, it will
* modify the execution path and no need to single
* stepping. Let's just reset current kprobe and exit.
*/
if (!p->pre_handler || !p->pre_handler(p, regs)) {
kcb->kprobe_status = KPROBE_HIT_SS;
Expand All @@ -312,8 +312,8 @@ void __kprobes kprobe_handler(struct pt_regs *regs)
kcb->kprobe_status = KPROBE_HIT_SSDONE;
p->post_handler(p, regs, 0);
}
reset_current_kprobe();
}
reset_current_kprobe();
}
} else {
/*
Expand Down
10 changes: 5 additions & 5 deletions arch/arm64/kernel/probes/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,18 +395,18 @@ static void __kprobes kprobe_handler(struct pt_regs *regs)
/*
* If we have no pre-handler or it returned 0, we
* continue with normal processing. If we have a
* pre-handler and it returned non-zero, it prepped
* for calling the break_handler below on re-entry,
* so get out doing nothing more here.
* pre-handler and it returned non-zero, it will
* modify the execution path and no need to single
* stepping. Let's just reset current kprobe and exit.
*
* pre_handler can hit a breakpoint and can step thru
* before return, keep PSTATE D-flag enabled until
* pre_handler return back.
*/
if (!p->pre_handler || !p->pre_handler(p, regs)) {
setup_singlestep(p, regs, kcb, 0);
return;
}
} else
reset_current_kprobe();
}
}
/*
Expand Down
13 changes: 4 additions & 9 deletions arch/ia64/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,9 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
*/
break;
}

kretprobe_assert(ri, orig_ret_address, trampoline_address);

reset_current_kprobe();
kretprobe_hash_unlock(current, &flags);
preempt_enable_no_resched();

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist);
Expand Down Expand Up @@ -851,13 +848,11 @@ static int __kprobes pre_kprobes_handler(struct die_args *args)
set_current_kprobe(p, kcb);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;

if (p->pre_handler && p->pre_handler(p, regs))
/*
* Our pre-handler is specifically requesting that we just
* do a return. This is used for both the jprobe pre-handler
* and the kretprobe trampoline
*/
if (p->pre_handler && p->pre_handler(p, regs)) {
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}

#if !defined(CONFIG_PREEMPT)
if (p->ainsn.inst_flag == INST_FLAG_BOOSTABLE && !p->post_handler) {
Expand Down
4 changes: 2 additions & 2 deletions arch/mips/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)

if (p->pre_handler && p->pre_handler(p, regs)) {
/* handler has already set things up, so skip ss setup */
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}

Expand Down Expand Up @@ -543,9 +545,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p,
kretprobe_assert(ri, orig_ret_address, trampoline_address);
instruction_pointer(regs) = orig_ret_address;

reset_current_kprobe();
kretprobe_hash_unlock(current, &flags);
preempt_enable_no_resched();

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist);
Expand Down
19 changes: 6 additions & 13 deletions arch/powerpc/kernel/kprobes-ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ void kprobe_ftrace_handler(unsigned long nip, unsigned long parent_nip,
struct kprobe *p;
struct kprobe_ctlblk *kcb;

preempt_disable();

p = get_kprobe((kprobe_opcode_t *)nip);
if (unlikely(!p) || kprobe_disabled(p))
goto end;
return;

kcb = get_kprobe_ctlblk();
if (kprobe_running()) {
Expand All @@ -60,18 +58,13 @@ void kprobe_ftrace_handler(unsigned long nip, unsigned long parent_nip,
kcb->kprobe_status = KPROBE_HIT_SSDONE;
p->post_handler(p, regs, 0);
}
__this_cpu_write(current_kprobe, NULL);
} else {
/*
* If pre_handler returns !0, it sets regs->nip and
* resets current kprobe. In this case, we should not
* re-enable preemption.
*/
return;
}
/*
* If pre_handler returns !0, it changes regs->nip. We have to
* skip emulating post_handler.
*/
__this_cpu_write(current_kprobe, NULL);
}
end:
preempt_enable_no_resched();
}
NOKPROBE_SYMBOL(kprobe_ftrace_handler);

Expand Down
7 changes: 5 additions & 2 deletions arch/powerpc/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -358,9 +358,12 @@ int kprobe_handler(struct pt_regs *regs)

kcb->kprobe_status = KPROBE_HIT_ACTIVE;
set_current_kprobe(p, regs, kcb);
if (p->pre_handler && p->pre_handler(p, regs))
/* handler has already set things up, so skip ss setup */
if (p->pre_handler && p->pre_handler(p, regs)) {
/* handler changed execution path, so skip ss setup */
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}

if (p->ainsn.boostable >= 0) {
ret = try_to_emulate(p, regs);
Expand Down
7 changes: 4 additions & 3 deletions arch/s390/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,11 @@ static int kprobe_handler(struct pt_regs *regs)
*/
push_kprobe(kcb, p);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
if (p->pre_handler && p->pre_handler(p, regs))
if (p->pre_handler && p->pre_handler(p, regs)) {
pop_kprobe(kcb);
preempt_enable_no_resched();
return 1;
}
kcb->kprobe_status = KPROBE_HIT_SS;
}
enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn);
Expand Down Expand Up @@ -431,9 +434,7 @@ static int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)

regs->psw.addr = orig_ret_address;

pop_kprobe(get_kprobe_ctlblk());
kretprobe_hash_unlock(current, &flags);
preempt_enable_no_resched();

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist);
Expand Down
7 changes: 4 additions & 3 deletions arch/sh/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,12 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
set_current_kprobe(p, regs, kcb);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;

if (p->pre_handler && p->pre_handler(p, regs))
if (p->pre_handler && p->pre_handler(p, regs)) {
/* handler has already set things up, so skip ss setup */
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}

prepare_singlestep(p, regs);
kcb->kprobe_status = KPROBE_HIT_SS;
Expand Down Expand Up @@ -352,8 +355,6 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
regs->pc = orig_ret_address;
kretprobe_hash_unlock(current, &flags);

preempt_enable_no_resched();

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist);
kfree(ri);
Expand Down
7 changes: 4 additions & 3 deletions arch/sparc/kernel/kprobes.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,11 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)

set_current_kprobe(p, regs, kcb);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
if (p->pre_handler && p->pre_handler(p, regs))
if (p->pre_handler && p->pre_handler(p, regs)) {
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}

prepare_singlestep(p, regs, kcb);
kcb->kprobe_status = KPROBE_HIT_SS;
Expand Down Expand Up @@ -508,9 +511,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p,
regs->tpc = orig_ret_address;
regs->tnpc = orig_ret_address + 4;

reset_current_kprobe();
kretprobe_hash_unlock(current, &flags);
preempt_enable_no_resched();

hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
hlist_del(&ri->hlist);
Expand Down
4 changes: 4 additions & 0 deletions arch/x86/kernel/kprobes/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,10 @@ int kprobe_int3_handler(struct pt_regs *regs)
*/
if (!p->pre_handler || !p->pre_handler(p, regs))
setup_singlestep(p, regs, kcb, 0);
else {
reset_current_kprobe();
preempt_enable_no_resched();
}
return 1;
}
} else if (*addr != BREAKPOINT_INSTRUCTION) {
Expand Down
9 changes: 3 additions & 6 deletions arch/x86/kernel/kprobes/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
/* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */
regs->ip = ip + sizeof(kprobe_opcode_t);

/* To emulate trap based kprobes, preempt_disable here */
preempt_disable();
__this_cpu_write(current_kprobe, p);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
if (!p->pre_handler || !p->pre_handler(p, regs)) {
Expand All @@ -60,13 +58,12 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
p->post_handler(p, regs, 0);
}
regs->ip = orig_ip;
__this_cpu_write(current_kprobe, NULL);
preempt_enable_no_resched();
}
/*
* If pre_handler returns !0, it sets regs->ip and
* resets current kprobe, and keep preempt count +1.
* If pre_handler returns !0, it changes regs->ip. We have to
* skip emulating post_handler.
*/
__this_cpu_write(current_kprobe, NULL);
}
}
NOKPROBE_SYMBOL(kprobe_ftrace_handler);
Expand Down
3 changes: 0 additions & 3 deletions kernel/fail_function.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,6 @@ static int fei_kprobe_handler(struct kprobe *kp, struct pt_regs *regs)
if (should_fail(&fei_fault_attr, 1)) {
regs_set_return_value(regs, attr->retval);
override_function_with_return(regs);
/* Kprobe specific fixup */
reset_current_kprobe();
preempt_enable_no_resched();
return 1;
}

Expand Down
11 changes: 3 additions & 8 deletions kernel/trace/trace_kprobe.c
Original file line number Diff line number Diff line change
Expand Up @@ -1217,16 +1217,11 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs)

/*
* We need to check and see if we modified the pc of the
* pt_regs, and if so clear the kprobe and return 1 so that we
* don't do the single stepping.
* The ftrace kprobe handler leaves it up to us to re-enable
* preemption here before returning if we've modified the ip.
* pt_regs, and if so return 1 so that we don't do the
* single stepping.
*/
if (orig_ip != instruction_pointer(regs)) {
reset_current_kprobe();
preempt_enable_no_resched();
if (orig_ip != instruction_pointer(regs))
return 1;
}
if (!ret)
return 0;
}
Expand Down

0 comments on commit cce188b

Please sign in to comment.