diff options
author | Ananth N Mavinakayanahalli <ananth@in.ibm.com> | 2009-09-15 01:13:07 -0400 |
---|---|---|
committer | Frederic Weisbecker <fweisbec@gmail.com> | 2009-09-16 22:24:57 -0400 |
commit | 1f0ab40976460bc4673fa204ce917a725185d8f2 (patch) | |
tree | d854f402352917359729eb35e73bc34ee227c875 /kernel/kprobes.c | |
parent | 5a0d9050db4d1147722b42afef9011251b2651ee (diff) |
kprobes: Prevent re-registration of the same kprobe
Prevent re-registration of the same kprobe. This situation, though
unlikely, needs to be flagged since it can lead to a system crash if
it's not handled.
The core change itself is small, but the helper routine needed to be
moved around a bit; hence the diffstat.
Signed-off-by: Ananth N Mavinakayanahalli<ananth@in.ibm.com>
Acked-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Jim Keniston <jkenisto@us.ibm.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Frank Ch. Eigler <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Jason Baron <jbaron@redhat.com>
Cc: K.Prasad <prasad@linux.vnet.ibm.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <20090915051307.GB26458@in.ibm.com>
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
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 | */ |