Commit 71f6e58e authored by Naveen N. Rao's avatar Naveen N. Rao Committed by Michael Ellerman

powerpc/kprobes: Convert __kprobes to NOKPROBE_SYMBOL()

Along similar lines as commit 9326638c ("kprobes, x86: Use NOKPROBE_SYMBOL()
instead of __kprobes annotation"), convert __kprobes annotation to either
NOKPROBE_SYMBOL() or nokprobe_inline. The latter forces inlining, in which case
the caller needs to be added to NOKPROBE_SYMBOL().

Also:
 - blacklist arch_deref_entry_point(), and
 - convert a few regular inlines to nokprobe_inline in lib/sstep.c

A key benefit is the ability to detect such symbols as being
blacklisted. Before this patch:

  $ cat /sys/kernel/debug/kprobes/blacklist | grep read_mem
  $ perf probe read_mem
  Failed to write event: Invalid argument
    Error: Failed to add events.
  $ dmesg | tail -1
  [ 3736.112815] Could not insert probe at _text+10014968: -22

After patch:
  $ cat /sys/kernel/debug/kprobes/blacklist | grep read_mem
  0xc000000000072b50-0xc000000000072d20	read_mem
  $ perf probe read_mem
  read_mem is blacklisted function, skip it.
  Added new events:
    (null):(null)        (on read_mem)
    probe:read_mem       (on read_mem)

  You can now use it in all perf tools, such as:

	  perf record -e probe:read_mem -aR sleep 1

  $ grep " read_mem" /proc/kallsyms
  c000000000072b50 t read_mem
  c0000000005f3b40 t read_mem
  $ cat /sys/kernel/debug/kprobes/list
  c0000000005f3b48  k  read_mem+0x8    [DISABLED]
Acked-by: default avatarMasami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: default avatarNaveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
[mpe: Minor change log formatting, fix up some conflicts]
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent 700e6437
...@@ -42,7 +42,7 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); ...@@ -42,7 +42,7 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
int __kprobes arch_prepare_kprobe(struct kprobe *p) int arch_prepare_kprobe(struct kprobe *p)
{ {
int ret = 0; int ret = 0;
kprobe_opcode_t insn = *p->addr; kprobe_opcode_t insn = *p->addr;
...@@ -74,30 +74,34 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) ...@@ -74,30 +74,34 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
p->ainsn.boostable = 0; p->ainsn.boostable = 0;
return ret; return ret;
} }
NOKPROBE_SYMBOL(arch_prepare_kprobe);
void __kprobes arch_arm_kprobe(struct kprobe *p) void arch_arm_kprobe(struct kprobe *p)
{ {
*p->addr = BREAKPOINT_INSTRUCTION; *p->addr = BREAKPOINT_INSTRUCTION;
flush_icache_range((unsigned long) p->addr, flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t)); (unsigned long) p->addr + sizeof(kprobe_opcode_t));
} }
NOKPROBE_SYMBOL(arch_arm_kprobe);
void __kprobes arch_disarm_kprobe(struct kprobe *p) void arch_disarm_kprobe(struct kprobe *p)
{ {
*p->addr = p->opcode; *p->addr = p->opcode;
flush_icache_range((unsigned long) p->addr, flush_icache_range((unsigned long) p->addr,
(unsigned long) p->addr + sizeof(kprobe_opcode_t)); (unsigned long) p->addr + sizeof(kprobe_opcode_t));
} }
NOKPROBE_SYMBOL(arch_disarm_kprobe);
void __kprobes arch_remove_kprobe(struct kprobe *p) void arch_remove_kprobe(struct kprobe *p)
{ {
if (p->ainsn.insn) { if (p->ainsn.insn) {
free_insn_slot(p->ainsn.insn, 0); free_insn_slot(p->ainsn.insn, 0);
p->ainsn.insn = NULL; p->ainsn.insn = NULL;
} }
} }
NOKPROBE_SYMBOL(arch_remove_kprobe);
static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) static nokprobe_inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
{ {
enable_single_step(regs); enable_single_step(regs);
...@@ -110,37 +114,37 @@ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs) ...@@ -110,37 +114,37 @@ static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
regs->nip = (unsigned long)p->ainsn.insn; regs->nip = (unsigned long)p->ainsn.insn;
} }
static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) static nokprobe_inline void save_previous_kprobe(struct kprobe_ctlblk *kcb)
{ {
kcb->prev_kprobe.kp = kprobe_running(); kcb->prev_kprobe.kp = kprobe_running();
kcb->prev_kprobe.status = kcb->kprobe_status; kcb->prev_kprobe.status = kcb->kprobe_status;
kcb->prev_kprobe.saved_msr = kcb->kprobe_saved_msr; kcb->prev_kprobe.saved_msr = kcb->kprobe_saved_msr;
} }
static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb) static nokprobe_inline void restore_previous_kprobe(struct kprobe_ctlblk *kcb)
{ {
__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp); __this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
kcb->kprobe_status = kcb->prev_kprobe.status; kcb->kprobe_status = kcb->prev_kprobe.status;
kcb->kprobe_saved_msr = kcb->prev_kprobe.saved_msr; kcb->kprobe_saved_msr = kcb->prev_kprobe.saved_msr;
} }
static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs, static nokprobe_inline void set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
struct kprobe_ctlblk *kcb) struct kprobe_ctlblk *kcb)
{ {
__this_cpu_write(current_kprobe, p); __this_cpu_write(current_kprobe, p);
kcb->kprobe_saved_msr = regs->msr; kcb->kprobe_saved_msr = regs->msr;
} }
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
struct pt_regs *regs)
{ {
ri->ret_addr = (kprobe_opcode_t *)regs->link; ri->ret_addr = (kprobe_opcode_t *)regs->link;
/* Replace the return addr with trampoline addr */ /* Replace the return addr with trampoline addr */
regs->link = (unsigned long)kretprobe_trampoline; regs->link = (unsigned long)kretprobe_trampoline;
} }
NOKPROBE_SYMBOL(arch_prepare_kretprobe);
int __kprobes kprobe_handler(struct pt_regs *regs) int kprobe_handler(struct pt_regs *regs)
{ {
struct kprobe *p; struct kprobe *p;
int ret = 0; int ret = 0;
...@@ -273,6 +277,7 @@ int __kprobes kprobe_handler(struct pt_regs *regs) ...@@ -273,6 +277,7 @@ int __kprobes kprobe_handler(struct pt_regs *regs)
preempt_enable_no_resched(); preempt_enable_no_resched();
return ret; return ret;
} }
NOKPROBE_SYMBOL(kprobe_handler);
/* /*
* Function return probe trampoline: * Function return probe trampoline:
...@@ -290,8 +295,7 @@ asm(".global kretprobe_trampoline\n" ...@@ -290,8 +295,7 @@ asm(".global kretprobe_trampoline\n"
/* /*
* Called when the probe at kretprobe trampoline is hit * Called when the probe at kretprobe trampoline is hit
*/ */
static int __kprobes trampoline_probe_handler(struct kprobe *p, static int trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
struct pt_regs *regs)
{ {
struct kretprobe_instance *ri = NULL; struct kretprobe_instance *ri = NULL;
struct hlist_head *head, empty_rp; struct hlist_head *head, empty_rp;
...@@ -360,6 +364,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, ...@@ -360,6 +364,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p,
*/ */
return 1; return 1;
} }
NOKPROBE_SYMBOL(trampoline_probe_handler);
/* /*
* Called after single-stepping. p->addr is the address of the * Called after single-stepping. p->addr is the address of the
...@@ -369,7 +374,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p, ...@@ -369,7 +374,7 @@ static int __kprobes trampoline_probe_handler(struct kprobe *p,
* single-stepped a copy of the instruction. The address of this * single-stepped a copy of the instruction. The address of this
* copy is p->ainsn.insn. * copy is p->ainsn.insn.
*/ */
int __kprobes kprobe_post_handler(struct pt_regs *regs) int kprobe_post_handler(struct pt_regs *regs)
{ {
struct kprobe *cur = kprobe_running(); struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
...@@ -409,8 +414,9 @@ int __kprobes kprobe_post_handler(struct pt_regs *regs) ...@@ -409,8 +414,9 @@ int __kprobes kprobe_post_handler(struct pt_regs *regs)
return 1; return 1;
} }
NOKPROBE_SYMBOL(kprobe_post_handler);
int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
{ {
struct kprobe *cur = kprobe_running(); struct kprobe *cur = kprobe_running();
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
...@@ -473,13 +479,15 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) ...@@ -473,13 +479,15 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
} }
return 0; return 0;
} }
NOKPROBE_SYMBOL(kprobe_fault_handler);
unsigned long arch_deref_entry_point(void *entry) unsigned long arch_deref_entry_point(void *entry)
{ {
return ppc_global_function_entry(entry); return ppc_global_function_entry(entry);
} }
NOKPROBE_SYMBOL(arch_deref_entry_point);
int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
{ {
struct jprobe *jp = container_of(p, struct jprobe, kp); struct jprobe *jp = container_of(p, struct jprobe, kp);
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
...@@ -496,17 +504,20 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs) ...@@ -496,17 +504,20 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
return 1; return 1;
} }
NOKPROBE_SYMBOL(setjmp_pre_handler);
void __used __kprobes jprobe_return(void) void __used jprobe_return(void)
{ {
asm volatile("trap" ::: "memory"); asm volatile("trap" ::: "memory");
} }
NOKPROBE_SYMBOL(jprobe_return);
static void __used __kprobes jprobe_return_end(void) static void __used jprobe_return_end(void)
{ {
}; }
NOKPROBE_SYMBOL(jprobe_return_end);
int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
{ {
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
...@@ -519,6 +530,7 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) ...@@ -519,6 +530,7 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
preempt_enable_no_resched(); preempt_enable_no_resched();
return 1; return 1;
} }
NOKPROBE_SYMBOL(longjmp_break_handler);
static struct kprobe trampoline_p = { static struct kprobe trampoline_p = {
.addr = (kprobe_opcode_t *) &kretprobe_trampoline, .addr = (kprobe_opcode_t *) &kretprobe_trampoline,
...@@ -530,10 +542,11 @@ int __init arch_init_kprobes(void) ...@@ -530,10 +542,11 @@ int __init arch_init_kprobes(void)
return register_kprobe(&trampoline_p); return register_kprobe(&trampoline_p);
} }
int __kprobes arch_trampoline_kprobe(struct kprobe *p) int arch_trampoline_kprobe(struct kprobe *p)
{ {
if (p->addr == (kprobe_opcode_t *)&kretprobe_trampoline) if (p->addr == (kprobe_opcode_t *)&kretprobe_trampoline)
return 1; return 1;
return 0; return 0;
} }
NOKPROBE_SYMBOL(arch_trampoline_kprobe);
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/mm.h> #include <linux/mm.h>
...@@ -59,7 +60,7 @@ bool is_offset_in_branch_range(long offset) ...@@ -59,7 +60,7 @@ bool is_offset_in_branch_range(long offset)
* Helper to check if a given instruction is a conditional branch * Helper to check if a given instruction is a conditional branch
* Derived from the conditional checks in analyse_instr() * Derived from the conditional checks in analyse_instr()
*/ */
bool __kprobes is_conditional_branch(unsigned int instr) bool is_conditional_branch(unsigned int instr)
{ {
unsigned int opcode = instr >> 26; unsigned int opcode = instr >> 26;
...@@ -75,6 +76,7 @@ bool __kprobes is_conditional_branch(unsigned int instr) ...@@ -75,6 +76,7 @@ bool __kprobes is_conditional_branch(unsigned int instr)
} }
return false; return false;
} }
NOKPROBE_SYMBOL(is_conditional_branch);
unsigned int create_branch(const unsigned int *addr, unsigned int create_branch(const unsigned int *addr,
unsigned long target, int flags) unsigned long target, int flags)
......
This diff is collapsed.
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