Loading arch/parisc/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ config PARISC select NEED_SG_DMA_LENGTH select HAVE_ARCH_KGDB select HAVE_KPROBES select HAVE_KRETPROBES help The PA-RISC microprocessor is designed by Hewlett-Packard and used Loading arch/parisc/kernel/kprobes.c +109 −1 Original line number Diff line number Diff line Loading @@ -172,6 +172,112 @@ int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs) return 1; } static inline void kretprobe_trampoline(void) { asm volatile("nop"); asm volatile("nop"); } static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs); static struct kprobe trampoline_p = { .pre_handler = trampoline_probe_handler }; static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) { struct kretprobe_instance *ri = NULL; struct hlist_head *head, empty_rp; struct hlist_node *tmp; unsigned long flags, orig_ret_address = 0; unsigned long trampoline_address = (unsigned long)trampoline_p.addr; kprobe_opcode_t *correct_ret_addr = NULL; INIT_HLIST_HEAD(&empty_rp); kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given * task either because multiple functions in the call path have * a return probe installed on them, and/or more than one return * probe was registered for a target function. * * We can handle this because: * - instances are always inserted at the head of the list * - when multiple return probes are registered for the same * function, the first instance's ret_addr will point to the * real return address, and all the rest will point to * kretprobe_trampoline */ hlist_for_each_entry_safe(ri, tmp, head, hlist) { if (ri->task != current) /* another task is sharing our hash bucket */ continue; orig_ret_address = (unsigned long)ri->ret_addr; if (orig_ret_address != trampoline_address) /* * This is the real return address. Any other * instances associated with this task are for * other calls deeper on the call stack */ break; } kretprobe_assert(ri, orig_ret_address, trampoline_address); correct_ret_addr = ri->ret_addr; hlist_for_each_entry_safe(ri, tmp, head, hlist) { if (ri->task != current) /* another task is sharing our hash bucket */ continue; orig_ret_address = (unsigned long)ri->ret_addr; if (ri->rp && ri->rp->handler) { __this_cpu_write(current_kprobe, &ri->rp->kp); get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; ri->ret_addr = correct_ret_addr; ri->rp->handler(ri, regs); __this_cpu_write(current_kprobe, NULL); } recycle_rp_inst(ri, &empty_rp); if (orig_ret_address != trampoline_address) /* * This is the real return address. Any other * instances associated with this task are for * other calls deeper on the call stack */ break; } kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); } instruction_pointer_set(regs, orig_ret_address); return 1; } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { ri->ret_addr = (kprobe_opcode_t *)regs->gr[2]; /* Replace the return addr with trampoline addr. */ regs->gr[2] = (unsigned long)trampoline_p.addr; } int __kprobes arch_trampoline_kprobe(struct kprobe *p) { return p->addr == trampoline_p.addr; } bool arch_kprobe_on_func_entry(unsigned long offset) { return !offset; Loading @@ -179,5 +285,7 @@ bool arch_kprobe_on_func_entry(unsigned long offset) int __init arch_init_kprobes(void) { return 0; trampoline_p.addr = (kprobe_opcode_t *) dereference_function_descriptor(kretprobe_trampoline); return register_kprobe(&trampoline_p); } Loading
arch/parisc/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ config PARISC select NEED_SG_DMA_LENGTH select HAVE_ARCH_KGDB select HAVE_KPROBES select HAVE_KRETPROBES help The PA-RISC microprocessor is designed by Hewlett-Packard and used Loading
arch/parisc/kernel/kprobes.c +109 −1 Original line number Diff line number Diff line Loading @@ -172,6 +172,112 @@ int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs) return 1; } static inline void kretprobe_trampoline(void) { asm volatile("nop"); asm volatile("nop"); } static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs); static struct kprobe trampoline_p = { .pre_handler = trampoline_probe_handler }; static int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs) { struct kretprobe_instance *ri = NULL; struct hlist_head *head, empty_rp; struct hlist_node *tmp; unsigned long flags, orig_ret_address = 0; unsigned long trampoline_address = (unsigned long)trampoline_p.addr; kprobe_opcode_t *correct_ret_addr = NULL; INIT_HLIST_HEAD(&empty_rp); kretprobe_hash_lock(current, &head, &flags); /* * It is possible to have multiple instances associated with a given * task either because multiple functions in the call path have * a return probe installed on them, and/or more than one return * probe was registered for a target function. * * We can handle this because: * - instances are always inserted at the head of the list * - when multiple return probes are registered for the same * function, the first instance's ret_addr will point to the * real return address, and all the rest will point to * kretprobe_trampoline */ hlist_for_each_entry_safe(ri, tmp, head, hlist) { if (ri->task != current) /* another task is sharing our hash bucket */ continue; orig_ret_address = (unsigned long)ri->ret_addr; if (orig_ret_address != trampoline_address) /* * This is the real return address. Any other * instances associated with this task are for * other calls deeper on the call stack */ break; } kretprobe_assert(ri, orig_ret_address, trampoline_address); correct_ret_addr = ri->ret_addr; hlist_for_each_entry_safe(ri, tmp, head, hlist) { if (ri->task != current) /* another task is sharing our hash bucket */ continue; orig_ret_address = (unsigned long)ri->ret_addr; if (ri->rp && ri->rp->handler) { __this_cpu_write(current_kprobe, &ri->rp->kp); get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE; ri->ret_addr = correct_ret_addr; ri->rp->handler(ri, regs); __this_cpu_write(current_kprobe, NULL); } recycle_rp_inst(ri, &empty_rp); if (orig_ret_address != trampoline_address) /* * This is the real return address. Any other * instances associated with this task are for * other calls deeper on the call stack */ break; } kretprobe_hash_unlock(current, &flags); hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) { hlist_del(&ri->hlist); kfree(ri); } instruction_pointer_set(regs, orig_ret_address); return 1; } void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs) { ri->ret_addr = (kprobe_opcode_t *)regs->gr[2]; /* Replace the return addr with trampoline addr. */ regs->gr[2] = (unsigned long)trampoline_p.addr; } int __kprobes arch_trampoline_kprobe(struct kprobe *p) { return p->addr == trampoline_p.addr; } bool arch_kprobe_on_func_entry(unsigned long offset) { return !offset; Loading @@ -179,5 +285,7 @@ bool arch_kprobe_on_func_entry(unsigned long offset) int __init arch_init_kprobes(void) { return 0; trampoline_p.addr = (kprobe_opcode_t *) dereference_function_descriptor(kretprobe_trampoline); return register_kprobe(&trampoline_p); }