diff options
Diffstat (limited to 'kernel/kprobes.c')
-rw-r--r-- | kernel/kprobes.c | 58 |
1 files changed, 38 insertions, 20 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 00d01b0f9fee..b946761f84bd 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -676,6 +676,40 @@ static kprobe_opcode_t __kprobes *kprobe_addr(struct kprobe *p) | |||
676 | return (kprobe_opcode_t *)(((char *)addr) + p->offset); | 676 | return (kprobe_opcode_t *)(((char *)addr) + p->offset); |
677 | } | 677 | } |
678 | 678 | ||
679 | /* Check passed kprobe is valid and return kprobe in kprobe_table. */ | ||
680 | static struct kprobe * __kprobes __get_valid_kprobe(struct kprobe *p) | ||
681 | { | ||
682 | struct kprobe *old_p, *list_p; | ||
683 | |||
684 | old_p = get_kprobe(p->addr); | ||
685 | if (unlikely(!old_p)) | ||
686 | return NULL; | ||
687 | |||
688 | if (p != old_p) { | ||
689 | list_for_each_entry_rcu(list_p, &old_p->list, list) | ||
690 | if (list_p == p) | ||
691 | /* kprobe p is a valid probe */ | ||
692 | goto valid; | ||
693 | return NULL; | ||
694 | } | ||
695 | valid: | ||
696 | return old_p; | ||
697 | } | ||
698 | |||
699 | /* Return error if the kprobe is being re-registered */ | ||
700 | static inline int check_kprobe_rereg(struct kprobe *p) | ||
701 | { | ||
702 | int ret = 0; | ||
703 | struct kprobe *old_p; | ||
704 | |||
705 | mutex_lock(&kprobe_mutex); | ||
706 | old_p = __get_valid_kprobe(p); | ||
707 | if (old_p) | ||
708 | ret = -EINVAL; | ||
709 | mutex_unlock(&kprobe_mutex); | ||
710 | return ret; | ||
711 | } | ||
712 | |||
679 | int __kprobes register_kprobe(struct kprobe *p) | 713 | int __kprobes register_kprobe(struct kprobe *p) |
680 | { | 714 | { |
681 | int ret = 0; | 715 | int ret = 0; |
@@ -688,6 +722,10 @@ int __kprobes register_kprobe(struct kprobe *p) | |||
688 | return -EINVAL; | 722 | return -EINVAL; |
689 | p->addr = addr; | 723 | p->addr = addr; |
690 | 724 | ||
725 | ret = check_kprobe_rereg(p); | ||
726 | if (ret) | ||
727 | return ret; | ||
728 | |||
691 | preempt_disable(); | 729 | preempt_disable(); |
692 | if (!kernel_text_address((unsigned long) p->addr) || | 730 | if (!kernel_text_address((unsigned long) p->addr) || |
693 | in_kprobes_functions((unsigned long) p->addr)) { | 731 | in_kprobes_functions((unsigned long) p->addr)) { |
@@ -757,26 +795,6 @@ out: | |||
757 | } | 795 | } |
758 | EXPORT_SYMBOL_GPL(register_kprobe); | 796 | EXPORT_SYMBOL_GPL(register_kprobe); |
759 | 797 | ||
760 | /* Check passed kprobe is valid and return kprobe in kprobe_table. */ | ||
761 | static struct kprobe * __kprobes __get_valid_kprobe(struct kprobe *p) | ||
762 | { | ||
763 | struct kprobe *old_p, *list_p; | ||
764 | |||
765 | old_p = get_kprobe(p->addr); | ||
766 | if (unlikely(!old_p)) | ||
767 | return NULL; | ||
768 | |||
769 | if (p != old_p) { | ||
770 | list_for_each_entry_rcu(list_p, &old_p->list, list) | ||
771 | if (list_p == p) | ||
772 | /* kprobe p is a valid probe */ | ||
773 | goto valid; | ||
774 | return NULL; | ||
775 | } | ||
776 | valid: | ||
777 | return old_p; | ||
778 | } | ||
779 | |||
780 | /* | 798 | /* |
781 | * Unregister a kprobe without a scheduler synchronization. | 799 | * Unregister a kprobe without a scheduler synchronization. |
782 | */ | 800 | */ |