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 | |
| 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')
| -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 | */ |
