Commit f438d914b220051d4cbc65cbc5d98e163c85c93b
Committed by
Linus Torvalds
1 parent
49dce689ad
Exists in
master
and in
4 other branches
kprobes: support kretprobe blacklist
Introduce architecture dependent kretprobe blacklists to prohibit users from inserting return probes on the function in which kprobes can be inserted but kretprobes can not. This patch also removes "__kprobes" mark from "__switch_to" on x86_64 and registers "__switch_to" to the blacklist on x86-64, because that mark is to prohibit user from inserting only kretprobe. Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: Prasanna S Panchamukhi <prasanna@in.ibm.com> Acked-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com> Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 17 changed files with 64 additions and 1 deletions Side-by-side Diff
- arch/avr32/kernel/kprobes.c
- arch/ia64/kernel/kprobes.c
- arch/powerpc/kernel/kprobes.c
- arch/s390/kernel/kprobes.c
- arch/sparc64/kernel/kprobes.c
- arch/x86/kernel/kprobes_32.c
- arch/x86/kernel/kprobes_64.c
- arch/x86/kernel/process_64.c
- include/asm-avr32/kprobes.h
- include/asm-ia64/kprobes.h
- include/asm-powerpc/kprobes.h
- include/asm-s390/kprobes.h
- include/asm-sparc64/kprobes.h
- include/asm-x86/kprobes_32.h
- include/asm-x86/kprobes_64.h
- include/linux/kprobes.h
- kernel/kprobes.c
arch/avr32/kernel/kprobes.c
arch/ia64/kernel/kprobes.c
... | ... | @@ -40,6 +40,8 @@ |
40 | 40 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
41 | 41 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
42 | 42 | |
43 | +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; | |
44 | + | |
43 | 45 | enum instruction_type {A, I, M, F, B, L, X, u}; |
44 | 46 | static enum instruction_type bundle_encoding[32][3] = { |
45 | 47 | { M, I, I }, /* 00 */ |
arch/powerpc/kernel/kprobes.c
... | ... | @@ -38,6 +38,8 @@ |
38 | 38 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
39 | 39 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
40 | 40 | |
41 | +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; | |
42 | + | |
41 | 43 | int __kprobes arch_prepare_kprobe(struct kprobe *p) |
42 | 44 | { |
43 | 45 | int ret = 0; |
arch/s390/kernel/kprobes.c
... | ... | @@ -33,6 +33,8 @@ |
33 | 33 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
34 | 34 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
35 | 35 | |
36 | +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; | |
37 | + | |
36 | 38 | int __kprobes arch_prepare_kprobe(struct kprobe *p) |
37 | 39 | { |
38 | 40 | /* Make sure the probe isn't going on a difficult instruction */ |
arch/sparc64/kernel/kprobes.c
... | ... | @@ -42,6 +42,8 @@ |
42 | 42 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
43 | 43 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
44 | 44 | |
45 | +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}}; | |
46 | + | |
45 | 47 | int __kprobes arch_prepare_kprobe(struct kprobe *p) |
46 | 48 | { |
47 | 49 | p->ainsn.insn[0] = *p->addr; |
arch/x86/kernel/kprobes_32.c
... | ... | @@ -41,6 +41,13 @@ |
41 | 41 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
42 | 42 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
43 | 43 | |
44 | +struct kretprobe_blackpoint kretprobe_blacklist[] = { | |
45 | + {"__switch_to", }, /* This function switches only current task, but | |
46 | + doesn't switch kernel stack.*/ | |
47 | + {NULL, NULL} /* Terminator */ | |
48 | +}; | |
49 | +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); | |
50 | + | |
44 | 51 | /* insert a jmp code */ |
45 | 52 | static __always_inline void set_jmp_op(void *from, void *to) |
46 | 53 | { |
arch/x86/kernel/kprobes_64.c
... | ... | @@ -48,6 +48,13 @@ |
48 | 48 | DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; |
49 | 49 | DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); |
50 | 50 | |
51 | +struct kretprobe_blackpoint kretprobe_blacklist[] = { | |
52 | + {"__switch_to", }, /* This function switches only current task, but | |
53 | + doesn't switch kernel stack.*/ | |
54 | + {NULL, NULL} /* Terminator */ | |
55 | +}; | |
56 | +const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist); | |
57 | + | |
51 | 58 | /* |
52 | 59 | * returns non-zero if opcode modifies the interrupt flag. |
53 | 60 | */ |
arch/x86/kernel/process_64.c
... | ... | @@ -581,7 +581,7 @@ |
581 | 581 | * |
582 | 582 | * Kprobes not supported here. Set the probe on schedule instead. |
583 | 583 | */ |
584 | -__kprobes struct task_struct * | |
584 | +struct task_struct * | |
585 | 585 | __switch_to(struct task_struct *prev_p, struct task_struct *next_p) |
586 | 586 | { |
587 | 587 | struct thread_struct *prev = &prev_p->thread, |
include/asm-avr32/kprobes.h
include/asm-ia64/kprobes.h
include/asm-powerpc/kprobes.h
include/asm-s390/kprobes.h
include/asm-sparc64/kprobes.h
include/asm-x86/kprobes_32.h
include/asm-x86/kprobes_64.h
include/linux/kprobes.h
... | ... | @@ -166,6 +166,12 @@ |
166 | 166 | struct task_struct *task; |
167 | 167 | }; |
168 | 168 | |
169 | +struct kretprobe_blackpoint { | |
170 | + const char *name; | |
171 | + void *addr; | |
172 | +}; | |
173 | +extern struct kretprobe_blackpoint kretprobe_blacklist[]; | |
174 | + | |
169 | 175 | static inline void kretprobe_assert(struct kretprobe_instance *ri, |
170 | 176 | unsigned long orig_ret_address, unsigned long trampoline_address) |
171 | 177 | { |
kernel/kprobes.c
... | ... | @@ -716,7 +716,19 @@ |
716 | 716 | int ret = 0; |
717 | 717 | struct kretprobe_instance *inst; |
718 | 718 | int i; |
719 | + void *addr = rp->kp.addr; | |
719 | 720 | |
721 | + if (kretprobe_blacklist_size) { | |
722 | + if (addr == NULL) | |
723 | + kprobe_lookup_name(rp->kp.symbol_name, addr); | |
724 | + addr += rp->kp.offset; | |
725 | + | |
726 | + for (i = 0; kretprobe_blacklist[i].name != NULL; i++) { | |
727 | + if (kretprobe_blacklist[i].addr == addr) | |
728 | + return -EINVAL; | |
729 | + } | |
730 | + } | |
731 | + | |
720 | 732 | rp->kp.pre_handler = pre_handler_kretprobe; |
721 | 733 | rp->kp.post_handler = NULL; |
722 | 734 | rp->kp.fault_handler = NULL; |
... | ... | @@ -792,6 +804,17 @@ |
792 | 804 | for (i = 0; i < KPROBE_TABLE_SIZE; i++) { |
793 | 805 | INIT_HLIST_HEAD(&kprobe_table[i]); |
794 | 806 | INIT_HLIST_HEAD(&kretprobe_inst_table[i]); |
807 | + } | |
808 | + | |
809 | + if (kretprobe_blacklist_size) { | |
810 | + /* lookup the function address from its name */ | |
811 | + for (i = 0; kretprobe_blacklist[i].name != NULL; i++) { | |
812 | + kprobe_lookup_name(kretprobe_blacklist[i].name, | |
813 | + kretprobe_blacklist[i].addr); | |
814 | + if (!kretprobe_blacklist[i].addr) | |
815 | + printk("kretprobe: lookup failed: %s\n", | |
816 | + kretprobe_blacklist[i].name); | |
817 | + } | |
795 | 818 | } |
796 | 819 | |
797 | 820 | /* By default, kprobes are enabled */ |