Commit f438d914b220051d4cbc65cbc5d98e163c85c93b

Authored by Masami Hiramatsu
Committed by Linus Torvalds
1 parent 49dce689ad

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
... ... @@ -22,6 +22,8 @@
22 22 static unsigned long kprobe_status;
23 23 static struct pt_regs jprobe_saved_regs;
24 24  
  25 +struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
  26 +
25 27 int __kprobes arch_prepare_kprobe(struct kprobe *p)
26 28 {
27 29 int ret = 0;
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
... ... @@ -17,6 +17,8 @@
17 17 #define BREAKPOINT_INSTRUCTION 0xd673 /* breakpoint */
18 18 #define MAX_INSN_SIZE 2
19 19  
  20 +#define kretprobe_blacklist_size 0
  21 +
20 22 #define arch_remove_kprobe(p) do { } while (0)
21 23  
22 24 /* Architecture specific copy of original instruction */
include/asm-ia64/kprobes.h
... ... @@ -83,6 +83,7 @@
83 83 };
84 84  
85 85 #define ARCH_SUPPORTS_KRETPROBES
  86 +#define kretprobe_blacklist_size 0
86 87  
87 88 #define SLOT0_OPCODE_SHIFT (37)
88 89 #define SLOT1_p1_OPCODE_SHIFT (37 - (64-46))
include/asm-powerpc/kprobes.h
... ... @@ -82,6 +82,7 @@
82 82  
83 83 #define ARCH_SUPPORTS_KRETPROBES
84 84 #define flush_insn_slot(p) do { } while (0)
  85 +#define kretprobe_blacklist_size 0
85 86  
86 87 void kretprobe_trampoline(void);
87 88 extern void arch_remove_kprobe(struct kprobe *p);
include/asm-s390/kprobes.h
... ... @@ -47,6 +47,7 @@
47 47 : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
48 48  
49 49 #define ARCH_SUPPORTS_KRETPROBES
  50 +#define kretprobe_blacklist_size 0
50 51  
51 52 #define KPROBE_SWAP_INST 0x10
52 53  
include/asm-sparc64/kprobes.h
... ... @@ -10,6 +10,8 @@
10 10 #define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */
11 11 #define MAX_INSN_SIZE 2
12 12  
  13 +#define kretprobe_blacklist_size 0
  14 +
13 15 #define arch_remove_kprobe(p) do {} while (0)
14 16  
15 17 #define flush_insn_slot(p) \
include/asm-x86/kprobes_32.h
... ... @@ -45,6 +45,8 @@
45 45 #define ARCH_SUPPORTS_KRETPROBES
46 46 #define flush_insn_slot(p) do { } while (0)
47 47  
  48 +extern const int kretprobe_blacklist_size;
  49 +
48 50 void arch_remove_kprobe(struct kprobe *p);
49 51 void kretprobe_trampoline(void);
50 52  
include/asm-x86/kprobes_64.h
... ... @@ -42,6 +42,7 @@
42 42 : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
43 43  
44 44 #define ARCH_SUPPORTS_KRETPROBES
  45 +extern const int kretprobe_blacklist_size;
45 46  
46 47 void kretprobe_trampoline(void);
47 48 extern void arch_remove_kprobe(struct kprobe *p);
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 {
... ... @@ -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 */