Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

Advertisement
[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Advertisement

Kernel v2.6.26-rc1 /kernel/kprobes.c

Filename:/kernel/kprobes.c
Lines Added:283
Lines Deleted:66
Also changed in: (Previous) 2.6.25-git20  2.6.25-git19  2.6.25-git18  2.6.25-git17  2.6.25-git16  2.6.25-git15 
(Following) 2.6.26-rc2  2.6.26-rc3  2.6.26-rc4  2.6.26-rc5  2.6.26-rc6  2.6.26-rc6-git1 

Location
[  2.6.26-rc1
  [  kernel
     o  kprobes.c

Patch

diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index fcfb580..1e0250c 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -72,6 +72,18 @@ DEFINE_MUTEX(kprobe_mutex);      /* Protects kprobe_table */
 DEFINE_SPINLOCK(kretprobe_lock);   /* Protects kretprobe_inst_table */
 static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL;
 
+/*
+ * Normally, functions that we'd want to prohibit kprobes in, are marked
+ * __kprobes. But, there are cases where such functions already belong to
+ * a different section (__sched for preempt_schedule)
+ *
+ * For such cases, we now have a blacklist
+ */
+struct kprobe_blackpoint kprobe_blacklist[] = {
+   {"preempt_schedule",},
+   {NULL}    /* Terminator */
+};
+
 #ifdef __ARCH_WANT_KPROBES_INSN_SLOT
 /*
  * kprobe->ainsn.insn points to the copy of the instruction to be
@@ -417,6 +429,21 @@ static inline void free_rp_inst(struct kretprobe *rp)
    }
 }
 
+static void __kprobes cleanup_rp_inst(struct kretprobe *rp)
+{
+   unsigned long flags;
+   struct kretprobe_instance *ri;
+   struct hlist_node *pos, *next;
+   /* No race here */
+   spin_lock_irqsave(&kretprobe_lock, flags);
+   hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) {
+      ri->rp = NULL;
+      hlist_del(&ri->uflist);
+   }
+   spin_unlock_irqrestore(&kretprobe_lock, flags);
+   free_rp_inst(rp);
+}
+
 /*
  * Keep all fields in the kprobe consistent
  */
@@ -492,9 +519,22 @@ static int __kprobes register_aggr_kprobe(struct kprobe *old_p,
 
 static int __kprobes in_kprobes_functions(unsigned long addr)
 {
+   struct kprobe_blackpoint *kb;
+
    if (addr >= (unsigned long)__kprobes_text_start &&
        addr < (unsigned long)__kprobes_text_end)
       return -EINVAL;
+   /*
+    * If there exists a kprobe_blacklist, verify and
+    * fail any probe registration in the prohibited area
+    */
+   for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
+      if (kb->start_addr) {
+         if (addr >= kb->start_addr &&
+             addr < (kb->start_addr + kb->range))
+            return -EINVAL;
+      }
+   }
    return 0;
 }
 
@@ -555,6 +595,7 @@ static int __kprobes __register_kprobe(struct kprobe *p,
    }
 
    p->nmissed = 0;
+   INIT_LIST_HEAD(&p->list);
    mutex_lock(&kprobe_mutex);
    old_p = get_kprobe(p->addr);
    if (old_p) {
@@ -581,35 +622,28 @@ out:
    return ret;
 }
 
-int __kprobes register_kprobe(struct kprobe *p)
-{
-   return __register_kprobe(p, (unsigned long)__builtin_return_address(0));
-}
-
-void __kprobes unregister_kprobe(struct kprobe *p)
+/*
+ * Unregister a kprobe without a scheduler synchronization.
+ */
+static int __kprobes __unregister_kprobe_top(struct kprobe *p)
 {
-   struct module *mod;
    struct kprobe *old_p, *list_p;
-   int cleanup_p;
 
-   mutex_lock(&kprobe_mutex);
    old_p = get_kprobe(p->addr);
-   if (unlikely(!old_p)) {
-      mutex_unlock(&kprobe_mutex);
-      return;
-   }
+   if (unlikely(!old_p))
+      return -EINVAL;
+
    if (p != old_p) {
       list_for_each_entry_rcu(list_p, &old_p->list, list)
          if (list_p == p)
          /* kprobe p is a valid probe */
             goto valid_p;
-      mutex_unlock(&kprobe_mutex);
-      return;
+      return -EINVAL;
    }
 valid_p:
    if (old_p == p ||
        (old_p->pre_handler == aggr_pre_handler &&
-        p->list.next == &old_p->list && p->list.prev == &old_p->list)) {
+        list_is_singular(&old_p->list))) {
       /*
        * Only probe on the hash list. Disarm only if kprobes are
        * enabled - otherwise, the breakpoint would already have
@@ -618,43 +652,97 @@ valid_p:
       if (kprobe_enabled)
          arch_disarm_kprobe(p);
       hlist_del_rcu(&old_p->hlist);
-      cleanup_p = 1;
    } else {
+      if (p->break_handler)
+         old_p->break_handler = NULL;
+      if (p->post_handler) {
+         list_for_each_entry_rcu(list_p, &old_p->list, list) {
+            if ((list_p != p) && (list_p->post_handler))
+               goto noclean;
+         }
+         old_p->post_handler = NULL;
+      }
+noclean:
       list_del_rcu(&p->list);
-      cleanup_p = 0;
    }
+   return 0;
+}
 
-   mutex_unlock(&kprobe_mutex);
+static void __kprobes __unregister_kprobe_bottom(struct kprobe *p)
+{
+   struct module *mod;
+   struct kprobe *old_p;
 
-   synchronize_sched();
    if (p->mod_refcounted) {
       mod = module_text_address((unsigned long)p->addr);
       if (mod)
          module_put(mod);
    }
 
-   if (cleanup_p) {
-      if (p != old_p) {
-         list_del_rcu(&p->list);
+   if (list_empty(&p->list) || list_is_singular(&p->list)) {
+      if (!list_empty(&p->list)) {
+         /* "p" is the last child of an aggr_kprobe */
+         old_p = list_entry(p->list.next, struct kprobe, list);
+         list_del(&p->list);
          kfree(old_p);
       }
       arch_remove_kprobe(p);
-   } else {
-      mutex_lock(&kprobe_mutex);
-      if (p->break_handler)
-         old_p->break_handler = NULL;
-      if (p->post_handler){
-         list_for_each_entry_rcu(list_p, &old_p->list, list){
-            if (list_p->post_handler){
-               cleanup_p = 2;
-               break;
-            }
-         }
-         if (cleanup_p == 0)
-            old_p->post_handler = NULL;
+   }
+}
+
+static int __register_kprobes(struct kprobe **kps, int num,
+   unsigned long called_from)
+{
+   int i, ret = 0;
+
+   if (num <= 0)
+      return -EINVAL;
+   for (i = 0; i < num; i++) {
+      ret = __register_kprobe(kps[i], called_from);
+      if (ret < 0 && i > 0) {
+         unregister_kprobes(kps, i);
+         break;
       }
-      mutex_unlock(&kprobe_mutex);
    }
+   return ret;
+}
+
+/*
+ * Registration and unregistration functions for kprobe.
+ */
+int __kprobes register_kprobe(struct kprobe *p)
+{
+   return __register_kprobes(&p, 1,
+              (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_kprobe(struct kprobe *p)
+{
+   unregister_kprobes(&p, 1);
+}
+
+int __kprobes register_kprobes(struct kprobe **kps, int num)
+{
+   return __register_kprobes(kps, num,
+              (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_kprobes(struct kprobe **kps, int num)
+{
+   int i;
+
+   if (num <= 0)
+      return;
+   mutex_lock(&kprobe_mutex);
+   for (i = 0; i < num; i++)
+      if (__unregister_kprobe_top(kps[i]) < 0)
+         kps[i]->addr = NULL;
+   mutex_unlock(&kprobe_mutex);
+
+   synchronize_sched();
+   for (i = 0; i < num; i++)
+      if (kps[i]->addr)
+         __unregister_kprobe_bottom(kps[i]);
 }
 
 static struct notifier_block kprobe_exceptions_nb = {
@@ -667,24 +755,69 @@ unsigned long __weak arch_deref_entry_point(void *entry)
    return (unsigned long)entry;
 }
 
-int __kprobes register_jprobe(struct jprobe *jp)
+static int __register_jprobes(struct jprobe **jps, int num,
+   unsigned long called_from)
 {
-   unsigned long addr = arch_deref_entry_point(jp->entry);
+   struct jprobe *jp;
+   int ret = 0, i;
 
-   if (!kernel_text_address(addr))
+   if (num <= 0)
       return -EINVAL;
+   for (i = 0; i < num; i++) {
+      unsigned long addr;
+      jp = jps[i];
+      addr = arch_deref_entry_point(jp->entry);
+
+      if (!kernel_text_address(addr))
+         ret = -EINVAL;
+      else {
+         /* Todo: Verify probepoint is a function entry point */
+         jp->kp.pre_handler = setjmp_pre_handler;
+         jp->kp.break_handler = longjmp_break_handler;
+         ret = __register_kprobe(&jp->kp, called_from);
+      }
+      if (ret < 0 && i > 0) {
+         unregister_jprobes(jps, i);
+         break;
+      }
+   }
+   return ret;
+}
 
-   /* Todo: Verify probepoint is a function entry point */
-   jp->kp.pre_handler = setjmp_pre_handler;
-   jp->kp.break_handler = longjmp_break_handler;
-
-   return __register_kprobe(&jp->kp,
+int __kprobes register_jprobe(struct jprobe *jp)
+{
+   return __register_jprobes(&jp, 1,
       (unsigned long)__builtin_return_address(0));
 }
 
 void __kprobes unregister_jprobe(struct jprobe *jp)
 {
-   unregister_kprobe(&jp->kp);
+   unregister_jprobes(&jp, 1);
+}
+
+int __kprobes register_jprobes(struct jprobe **jps, int num)
+{
+   return __register_jprobes(jps, num,
+      (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_jprobes(struct jprobe **jps, int num)
+{
+   int i;
+
+   if (num <= 0)
+      return;
+   mutex_lock(&kprobe_mutex);
+   for (i = 0; i < num; i++)
+      if (__unregister_kprobe_top(&jps[i]->kp) < 0)
+         jps[i]->kp.addr = NULL;
+   mutex_unlock(&kprobe_mutex);
+
+   synchronize_sched();
+   for (i = 0; i < num; i++) {
+      if (jps[i]->kp.addr)
+         __unregister_kprobe_bottom(&jps[i]->kp);
+   }
 }
 
 #ifdef CONFIG_KRETPROBES
@@ -725,7 +858,8 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
    return 0;
 }
 
-int __kprobes register_kretprobe(struct kretprobe *rp)
+static int __kprobes __register_kretprobe(struct kretprobe *rp,
+                 unsigned long called_from)
 {
    int ret = 0;
    struct kretprobe_instance *inst;
@@ -771,46 +905,101 @@ int __kprobes register_kretprobe(struct kretprobe *rp)
 
    rp->nmissed = 0;
    /* Establish function entry probe point */
-   if ((ret = __register_kprobe(&rp->kp,
-      (unsigned long)__builtin_return_address(0))) != 0)
+   ret = __register_kprobe(&rp->kp, called_from);
+   if (ret != 0)
       free_rp_inst(rp);
    return ret;
 }
 
+static int __register_kretprobes(struct kretprobe **rps, int num,
+   unsigned long called_from)
+{
+   int ret = 0, i;
+
+   if (num <= 0)
+      return -EINVAL;
+   for (i = 0; i < num; i++) {
+      ret = __register_kretprobe(rps[i], called_from);
+      if (ret < 0 && i > 0) {
+         unregister_kretprobes(rps, i);
+         break;
+      }
+   }
+   return ret;
+}
+
+int __kprobes register_kretprobe(struct kretprobe *rp)
+{
+   return __register_kretprobes(&rp, 1,
+         (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_kretprobe(struct kretprobe *rp)
+{
+   unregister_kretprobes(&rp, 1);
+}
+
+int __kprobes register_kretprobes(struct kretprobe **rps, int num)
+{
+   return __register_kretprobes(rps, num,
+         (unsigned long)__builtin_return_address(0));
+}
+
+void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+{
+   int i;
+
+   if (num <= 0)
+      return;
+   mutex_lock(&kprobe_mutex);
+   for (i = 0; i < num; i++)
+      if (__unregister_kprobe_top(&rps[i]->kp) < 0)
+         rps[i]->kp.addr = NULL;
+   mutex_unlock(&kprobe_mutex);
+
+   synchronize_sched();
+   for (i = 0; i < num; i++) {
+      if (rps[i]->kp.addr) {
+         __unregister_kprobe_bottom(&rps[i]->kp);
+         cleanup_rp_inst(rps[i]);
+      }
+   }
+}
+
 #else /* CONFIG_KRETPROBES */
 int __kprobes register_kretprobe(struct kretprobe *rp)
 {
    return -ENOSYS;
 }
 
-static int __kprobes pre_handler_kretprobe(struct kprobe *p,
-                  struct pt_regs *regs)
+int __kprobes register_kretprobes(struct kretprobe **rps, int num)
 {
-   return 0;
+   return -ENOSYS;
 }
-#endif /* CONFIG_KRETPROBES */
-
 void __kprobes unregister_kretprobe(struct kretprobe *rp)
 {
-   unsigned long flags;
-   struct kretprobe_instance *ri;
-   struct hlist_node *pos, *next;
+}
 
-   unregister_kprobe(&rp->kp);
+void __kprobes unregister_kretprobes(struct kretprobe **rps, int num)
+{
+}
 
-   /* No race here */
-   spin_lock_irqsave(&kretprobe_lock, flags);
-   hlist_for_each_entry_safe(ri, pos, next, &rp->used_instances, uflist) {
-      ri->rp = NULL;
-      hlist_del(&ri->uflist);
-   }
-   spin_unlock_irqrestore(&kretprobe_lock, flags);
-   free_rp_inst(rp);
+static int __kprobes pre_handler_kretprobe(struct kprobe *p,
+                  struct pt_regs *regs)
+{
+   return 0;
 }
 
+#endif /* CONFIG_KRETPROBES */
+
 static int __init init_kprobes(void)
 {
    int i, err = 0;
+   unsigned long offset = 0, size = 0;
+   char *modname, namebuf[128];
+   const char *symbol_name;
+   void *addr;
+   struct kprobe_blackpoint *kb;
 
    /* FIXME allocate the probe table, currently defined statically */
    /* initialize all list heads */
@@ -819,6 +1008,28 @@ static int __init init_kprobes(void)
       INIT_HLIST_HEAD(&kretprobe_inst_table[i]);
    }
 
+   /*
+    * Lookup and populate the kprobe_blacklist.
+    *
+    * Unlike the kretprobe blacklist, we'll need to determine
+    * the range of addresses that belong to the said functions,
+    * since a kprobe need not necessarily be at the beginning
+    * of a function.
+    */
+   for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
+      kprobe_lookup_name(kb->name, addr);
+      if (!addr)
+         continue;
+
+      kb->start_addr = (unsigned long)addr;
+      symbol_name = kallsyms_lookup(kb->start_addr,
+            &size, &offset, &modname, namebuf);
+      if (!symbol_name)
+         kb->range = 0;
+      else
+         kb->range = size;
+   }
+
    if (kretprobe_blacklist_size) {
       /* lookup the function address from its name */
       for (i = 0; kretprobe_blacklist[i].name != NULL; i++) {
@@ -1066,8 +1277,12 @@ module_init(init_kprobes);
 
 EXPORT_SYMBOL_GPL(register_kprobe);
 EXPORT_SYMBOL_GPL(unregister_kprobe);
+EXPORT_SYMBOL_GPL(register_kprobes);
+EXPORT_SYMBOL_GPL(unregister_kprobes);
 EXPORT_SYMBOL_GPL(register_jprobe);
 EXPORT_SYMBOL_GPL(unregister_jprobe);
+EXPORT_SYMBOL_GPL(register_jprobes);
+EXPORT_SYMBOL_GPL(unregister_jprobes);
 #ifdef CONFIG_KPROBES
 EXPORT_SYMBOL_GPL(jprobe_return);
 #endif
@@ -1075,4 +1290,6 @@ EXPORT_SYMBOL_GPL(jprobe_return);
 #ifdef CONFIG_KPROBES
 EXPORT_SYMBOL_GPL(register_kretprobe);
 EXPORT_SYMBOL_GPL(unregister_kretprobe);
+EXPORT_SYMBOL_GPL(register_kretprobes);
+EXPORT_SYMBOL_GPL(unregister_kretprobes);
 #endif


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.