kprobes: support kretprobe blacklist
authorMasami Hiramatsu <mhiramat@redhat.com>
Tue, 16 Oct 2007 08:27:49 +0000 (01:27 -0700)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Tue, 16 Oct 2007 16:43:10 +0000 (09:43 -0700)
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>
17 files changed:
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

index 4942ee662e0b53c14fb84c572a1902992433a031..20b1c9d8f945e1e68e994286728476f548d82bf2 100644 (file)
@@ -22,6 +22,8 @@ DEFINE_PER_CPU(struct kprobe *, current_kprobe);
 static unsigned long kprobe_status;
 static struct pt_regs jprobe_saved_regs;
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
        int ret = 0;
index 5dc98b5abcfbcc3de963e60faaa139d86d362170..5fd65d8302c8561e50cf12a576f0c54569c30d1d 100644 (file)
@@ -40,6 +40,8 @@ extern void jprobe_inst_return(void);
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
+
 enum instruction_type {A, I, M, F, B, L, X, u};
 static enum instruction_type bundle_encoding[32][3] = {
   { M, I, I },                         /* 00 */
index 440f5a87271faf8e9467e198db21677e49ceded6..5338e48557127ca02e8238f421f73bcb88ae33b0 100644 (file)
@@ -38,6 +38,8 @@
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
        int ret = 0;
index e40373d9fbce7ba975f3cbba412c70368988273d..c5549a20628450f31794d708c63f6fa80bab89ac 100644 (file)
@@ -33,6 +33,8 @@
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
        /* Make sure the probe isn't going on a difficult instruction */
index c93a15b785fa28322b5c254040ceb510904e7113..d94f901d321e91423e25f26fee5ccf3f57907c8f 100644 (file)
@@ -42,6 +42,8 @@
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {{NULL, NULL}};
+
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
        p->ainsn.insn[0] = *p->addr;
index 06b86e5617f62c739bdc4b448985d46462078273..90f778c04b3f1b847ea6c8653ae2f0c9503a8e39 100644 (file)
@@ -41,6 +41,13 @@ void jprobe_return_end(void);
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {
+       {"__switch_to", }, /* This function switches only current task, but
+                            doesn't switch kernel stack.*/
+       {NULL, NULL}    /* Terminator */
+};
+const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
+
 /* insert a jmp code */
 static __always_inline void set_jmp_op(void *from, void *to)
 {
index 7c16506d681fc56f985c65f2c4b22dea7a5019ea..681b801c5e26bf7be2a09c24134f590381214258 100644 (file)
@@ -48,6 +48,13 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p);
 DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
 
+struct kretprobe_blackpoint kretprobe_blacklist[] = {
+       {"__switch_to", }, /* This function switches only current task, but
+                             doesn't switch kernel stack.*/
+       {NULL, NULL}    /* Terminator */
+};
+const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
+
 /*
  * returns non-zero if opcode modifies the interrupt flag.
  */
index 7352d4b377e6d0f411bf1a6cf9d26e9266afacaa..6309b275cb9c176f61133fa7fc2b5585c9063fd4 100644 (file)
@@ -581,7 +581,7 @@ static inline void __switch_to_xtra(struct task_struct *prev_p,
  *
  * Kprobes not supported here. Set the probe on schedule instead.
  */
-__kprobes struct task_struct *
+struct task_struct *
 __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
 {
        struct thread_struct *prev = &prev_p->thread,
index 0f3e636e6e4dda8d36c89b50f697777f59bdf88d..996cb656474e267920ad6856f6445156349b0579 100644 (file)
@@ -17,6 +17,8 @@ typedef u16   kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION 0xd673  /* breakpoint */
 #define MAX_INSN_SIZE          2
 
+#define kretprobe_blacklist_size 0
+
 #define arch_remove_kprobe(p)  do { } while (0)
 
 /* Architecture specific copy of original instruction */
index 6c79edf24d73adc1572b885417394bcb6c02ae13..a93ce9ef07ff7a5497a4806d99df460030b080aa 100644 (file)
@@ -83,6 +83,7 @@ struct kprobe_ctlblk {
 };
 
 #define ARCH_SUPPORTS_KRETPROBES
+#define kretprobe_blacklist_size 0
 
 #define SLOT0_OPCODE_SHIFT     (37)
 #define SLOT1_p1_OPCODE_SHIFT  (37 - (64-46))
index c16973d5de6287dc4c14d1572065d747c719416e..afabad230dbb4f212d057449f03f3fec3b79d171 100644 (file)
@@ -82,6 +82,7 @@ typedef unsigned int kprobe_opcode_t;
 
 #define ARCH_SUPPORTS_KRETPROBES
 #define flush_insn_slot(p)     do { } while (0)
+#define kretprobe_blacklist_size 0
 
 void kretprobe_trampoline(void);
 extern void arch_remove_kprobe(struct kprobe *p);
index 8bc67cc9ffd2972a64f9da56564892e54d48d7c5..948db3d0d05c194d4715706fe4c932a781d23e4e 100644 (file)
@@ -47,6 +47,7 @@ typedef u16 kprobe_opcode_t;
        : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
 #define ARCH_SUPPORTS_KRETPROBES
+#define kretprobe_blacklist_size 0
 
 #define KPROBE_SWAP_INST       0x10
 
index a04145b77f9635e8a7a30504bef7f1b2cfdcc1e1..5020eaf67c292d10c34831cb71ce5ec421a6e8fd 100644 (file)
@@ -10,6 +10,8 @@ typedef u32 kprobe_opcode_t;
 #define BREAKPOINT_INSTRUCTION_2 0x91d02071 /* ta 0x71 */
 #define MAX_INSN_SIZE 2
 
+#define kretprobe_blacklist_size 0
+
 #define arch_remove_kprobe(p)  do {} while (0)
 
 #define flush_insn_slot(p)             \
index f2489d07ce88cdef0dec6d5d5d605a0d84a3c4f0..b772d5b386855cd4f3de9c52ab13f747f9257e25 100644 (file)
@@ -45,6 +45,8 @@ typedef u8 kprobe_opcode_t;
 #define ARCH_SUPPORTS_KRETPROBES
 #define flush_insn_slot(p)     do { } while (0)
 
+extern const int kretprobe_blacklist_size;
+
 void arch_remove_kprobe(struct kprobe *p);
 void kretprobe_trampoline(void);
 
index 3f495e5308b13499f4d7122e2bb90510bc293417..53f4d85073545641421291911acc3a319488868c 100644 (file)
@@ -42,6 +42,7 @@ typedef u8 kprobe_opcode_t;
        : (((unsigned long)current_thread_info()) + THREAD_SIZE - (ADDR)))
 
 #define ARCH_SUPPORTS_KRETPROBES
+extern const int kretprobe_blacklist_size;
 
 void kretprobe_trampoline(void);
 extern void arch_remove_kprobe(struct kprobe *p);
index 51464d12a4e5aa14b09a7a0d0b228055ba81b23a..81891581e89ba544a527aa994386374b4c1c2146 100644 (file)
@@ -166,6 +166,12 @@ struct kretprobe_instance {
        struct task_struct *task;
 };
 
+struct kretprobe_blackpoint {
+       const char *name;
+       void *addr;
+};
+extern struct kretprobe_blackpoint kretprobe_blacklist[];
+
 static inline void kretprobe_assert(struct kretprobe_instance *ri,
        unsigned long orig_ret_address, unsigned long trampoline_address)
 {
index f9798ff7899f4a610823fd641fe5e2dde0e746f7..e3a5d817ac9b0f07b514587a7ec03453461ab655 100644 (file)
@@ -716,6 +716,18 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
        int ret = 0;
        struct kretprobe_instance *inst;
        int i;
+       void *addr = rp->kp.addr;
+
+       if (kretprobe_blacklist_size) {
+               if (addr == NULL)
+                       kprobe_lookup_name(rp->kp.symbol_name, addr);
+               addr += rp->kp.offset;
+
+               for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
+                       if (kretprobe_blacklist[i].addr == addr)
+                               return -EINVAL;
+               }
+       }
 
        rp->kp.pre_handler = pre_handler_kretprobe;
        rp->kp.post_handler = NULL;
@@ -794,6 +806,17 @@ static int __init init_kprobes(void)
                INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
        }
 
+       if (kretprobe_blacklist_size) {
+               /* lookup the function address from its name */
+               for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
+                       kprobe_lookup_name(kretprobe_blacklist[i].name,
+                                          kretprobe_blacklist[i].addr);
+                       if (!kretprobe_blacklist[i].addr)
+                               printk("kretprobe: lookup failed: %s\n",
+                                      kretprobe_blacklist[i].name);
+               }
+       }
+
        /* By default, kprobes are enabled */
        kprobe_enabled = true;