sc92031: remove bogus unlikely()
[pandora-kernel.git] / kernel / kprobes.c
index fcfb580..1e0250c 100644 (file)
@@ -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