jprobes: make jprobes a little safer for users
[deliverable/linux.git] / arch / ia64 / kernel / kprobes.c
index 4d592ee9300b2739d6c82f569f0b1a02254ee09e..5dc98b5abcfbcc3de963e60faaa139d86d362170 100644 (file)
@@ -29,9 +29,9 @@
 #include <linux/slab.h>
 #include <linux/preempt.h>
 #include <linux/moduleloader.h>
+#include <linux/kdebug.h>
 
 #include <asm/pgtable.h>
-#include <asm/kdebug.h>
 #include <asm/sections.h>
 #include <asm/uaccess.h>
 
@@ -88,6 +88,7 @@ static void __kprobes update_kprobe_inst_flag(uint template, uint  slot,
 {
        p->ainsn.inst_flag = 0;
        p->ainsn.target_br_reg = 0;
+       p->ainsn.slot = slot;
 
        /* Check for Break instruction
         * Bits 37:40 Major opcode to be zero
@@ -127,10 +128,46 @@ static void __kprobes update_kprobe_inst_flag(uint template, uint  slot,
        return;
 }
 
+/*
+ * In this function we check to see if the instruction
+ * (qp) cmpx.crel.ctype p1,p2=r2,r3
+ * on which we are inserting kprobe is cmp instruction
+ * with ctype as unc.
+ */
+static uint __kprobes is_cmp_ctype_unc_inst(uint template, uint slot,
+                                           uint major_opcode,
+                                           unsigned long kprobe_inst)
+{
+       cmp_inst_t cmp_inst;
+       uint ctype_unc = 0;
+
+       if (!((bundle_encoding[template][slot] == I) ||
+               (bundle_encoding[template][slot] == M)))
+               goto out;
+
+       if (!((major_opcode == 0xC) || (major_opcode == 0xD) ||
+               (major_opcode == 0xE)))
+               goto out;
+
+       cmp_inst.l = kprobe_inst;
+       if ((cmp_inst.f.x2 == 0) || (cmp_inst.f.x2 == 1)) {
+               /* Integer compare - Register Register (A6 type)*/
+               if ((cmp_inst.f.tb == 0) && (cmp_inst.f.ta == 0)
+                               &&(cmp_inst.f.c == 1))
+                       ctype_unc = 1;
+       } else if ((cmp_inst.f.x2 == 2)||(cmp_inst.f.x2 == 3)) {
+               /* Integer compare - Immediate Register (A8 type)*/
+               if ((cmp_inst.f.ta == 0) &&(cmp_inst.f.c == 1))
+                       ctype_unc = 1;
+       }
+out:
+       return ctype_unc;
+}
+
 /*
  * In this function we check to see if the instruction
  * on which we are inserting kprobe is supported.
- * Returns 0 if supported
+ * Returns qp value if supported
  * Returns -EINVAL if unsupported
  */
 static int __kprobes unsupported_inst(uint template, uint  slot,
@@ -138,9 +175,21 @@ static int __kprobes unsupported_inst(uint template, uint  slot,
                                      unsigned long kprobe_inst,
                                      unsigned long addr)
 {
-       if (bundle_encoding[template][slot] == I) {
-               switch (major_opcode) {
-                       case 0x0: //I_UNIT_MISC_OPCODE:
+       int qp;
+
+       qp = kprobe_inst & 0x3f;
+       if (is_cmp_ctype_unc_inst(template, slot, major_opcode, kprobe_inst)) {
+               if (slot == 1 && qp)  {
+                       printk(KERN_WARNING "Kprobes on cmp unc"
+                                       "instruction on slot 1 at <0x%lx>"
+                                       "is not supported\n", addr);
+                       return -EINVAL;
+
+               }
+               qp = 0;
+       }
+       else if (bundle_encoding[template][slot] == I) {
+               if (major_opcode == 0) {
                        /*
                         * Check for Integer speculation instruction
                         * - Bit 33-35 to be equal to 0x1
@@ -148,10 +197,9 @@ static int __kprobes unsupported_inst(uint template, uint  slot,
                        if (((kprobe_inst >> 33) & 0x7) == 1) {
                                printk(KERN_WARNING
                                        "Kprobes on speculation inst at <0x%lx> not supported\n",
-                                       addr);
+                                               addr);
                                return -EINVAL;
                        }
-
                        /*
                         * IP relative mov instruction
                         *  - Bit 27-35 to be equal to 0x30
@@ -159,50 +207,74 @@ static int __kprobes unsupported_inst(uint template, uint  slot,
                        if (((kprobe_inst >> 27) & 0x1FF) == 0x30) {
                                printk(KERN_WARNING
                                        "Kprobes on \"mov r1=ip\" at <0x%lx> not supported\n",
-                                       addr);
+                                               addr);
                                return -EINVAL;
 
                        }
                }
+               else if ((major_opcode == 5) && !(kprobe_inst & (0xFUl << 33)) &&
+                               (kprobe_inst & (0x1UL << 12))) {
+                       /* test bit instructions, tbit,tnat,tf
+                        * bit 33-36 to be equal to 0
+                        * bit 12 to be equal to 1
+                        */
+                       if (slot == 1 && qp) {
+                               printk(KERN_WARNING "Kprobes on test bit"
+                                               "instruction on slot at <0x%lx>"
+                                               "is not supported\n", addr);
+                               return -EINVAL;
+                       }
+                       qp = 0;
+               }
        }
-       return 0;
-}
-
-
-/*
- * In this function we check to see if the instruction
- * (qp) cmpx.crel.ctype p1,p2=r2,r3
- * on which we are inserting kprobe is cmp instruction
- * with ctype as unc.
- */
-static uint __kprobes is_cmp_ctype_unc_inst(uint template, uint slot,
-                                           uint major_opcode,
-                                           unsigned long kprobe_inst)
-{
-       cmp_inst_t cmp_inst;
-       uint ctype_unc = 0;
-
-       if (!((bundle_encoding[template][slot] == I) ||
-               (bundle_encoding[template][slot] == M)))
-               goto out;
-
-       if (!((major_opcode == 0xC) || (major_opcode == 0xD) ||
-               (major_opcode == 0xE)))
-               goto out;
+       else if (bundle_encoding[template][slot] == B) {
+               if (major_opcode == 7) {
+                       /* IP-Relative Predict major code is 7 */
+                       printk(KERN_WARNING "Kprobes on IP-Relative"
+                                       "Predict is not supported\n");
+                       return -EINVAL;
+               }
+               else if (major_opcode == 2) {
+                       /* Indirect Predict, major code is 2
+                        * bit 27-32 to be equal to 10 or 11
+                        */
+                       int x6=(kprobe_inst >> 27) & 0x3F;
+                       if ((x6 == 0x10) || (x6 == 0x11)) {
+                               printk(KERN_WARNING "Kprobes on"
+                                       "Indirect Predict is not supported\n");
+                               return -EINVAL;
+                       }
+               }
+       }
+       /* kernel does not use float instruction, here for safety kprobe
+        * will judge whether it is fcmp/flass/float approximation instruction
+        */
+       else if (unlikely(bundle_encoding[template][slot] == F)) {
+               if ((major_opcode == 4 || major_opcode == 5) &&
+                               (kprobe_inst  & (0x1 << 12))) {
+                       /* fcmp/fclass unc instruction */
+                       if (slot == 1 && qp) {
+                               printk(KERN_WARNING "Kprobes on fcmp/fclass "
+                                       "instruction on slot at <0x%lx> "
+                                       "is not supported\n", addr);
+                               return -EINVAL;
 
-       cmp_inst.l = kprobe_inst;
-       if ((cmp_inst.f.x2 == 0) || (cmp_inst.f.x2 == 1)) {
-               /* Integere compare - Register Register (A6 type)*/
-               if ((cmp_inst.f.tb == 0) && (cmp_inst.f.ta == 0)
-                               &&(cmp_inst.f.c == 1))
-                       ctype_unc = 1;
-       } else if ((cmp_inst.f.x2 == 2)||(cmp_inst.f.x2 == 3)) {
-               /* Integere compare - Immediate Register (A8 type)*/
-               if ((cmp_inst.f.ta == 0) &&(cmp_inst.f.c == 1))
-                       ctype_unc = 1;
+                       }
+                       qp = 0;
+               }
+               if ((major_opcode == 0 || major_opcode == 1) &&
+                       (kprobe_inst & (0x1UL << 33))) {
+                       /* float Approximation instruction */
+                       if (slot == 1 && qp) {
+                               printk(KERN_WARNING "Kprobes on float Approx "
+                                       "instr at <0x%lx> is not supported\n",
+                                               addr);
+                               return -EINVAL;
+                       }
+                       qp = 0;
+               }
        }
-out:
-       return ctype_unc;
+       return qp;
 }
 
 /*
@@ -212,20 +284,17 @@ out:
 static void __kprobes prepare_break_inst(uint template, uint  slot,
                                         uint major_opcode,
                                         unsigned long kprobe_inst,
-                                        struct kprobe *p)
+                                        struct kprobe *p,
+                                        int qp)
 {
        unsigned long break_inst = BREAK_INST;
        bundle_t *bundle = &p->opcode.bundle;
 
        /*
         * Copy the original kprobe_inst qualifying predicate(qp)
-        * to the break instruction iff !is_cmp_ctype_unc_inst
-        * because for cmp instruction with ctype equal to unc,
-        * which is a special instruction always needs to be
-        * executed regradless of qp
+        * to the break instruction
         */
-       if (!is_cmp_ctype_unc_inst(template, slot, major_opcode, kprobe_inst))
-               break_inst |= (0x3f & kprobe_inst);
+       break_inst |= qp;
 
        switch (slot) {
          case 0:
@@ -296,25 +365,23 @@ static int __kprobes valid_kprobe_addr(int template, int slot,
                return -EINVAL;
        }
 
-       if (slot == 1 && bundle_encoding[template][1] != L) {
-               printk(KERN_WARNING "Inserting kprobes on slot #1 "
-                      "is not supported\n");
-               return -EINVAL;
-       }
-
        return 0;
 }
 
 static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
 {
-       kcb->prev_kprobe.kp = kprobe_running();
-       kcb->prev_kprobe.status = kcb->kprobe_status;
+       unsigned int i;
+       i = atomic_add_return(1, &kcb->prev_kprobe_index);
+       kcb->prev_kprobe[i-1].kp = kprobe_running();
+       kcb->prev_kprobe[i-1].status = kcb->kprobe_status;
 }
 
 static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
 {
-       __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
-       kcb->kprobe_status = kcb->prev_kprobe.status;
+       unsigned int i;
+       i = atomic_sub_return(1, &kcb->prev_kprobe_index);
+       __get_cpu_var(current_kprobe) = kcb->prev_kprobe[i].kp;
+       kcb->kprobe_status = kcb->prev_kprobe[i].status;
 }
 
 static void __kprobes set_current_kprobe(struct kprobe *p,
@@ -381,7 +448,8 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
                        break;
        }
 
-       BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+       kretprobe_assert(ri, orig_ret_address, trampoline_address);
+
        regs->cr_iip = orig_ret_address;
 
        reset_current_kprobe();
@@ -401,23 +469,13 @@ int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
 }
 
 /* Called with kretprobe_lock held */
-void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
                                      struct pt_regs *regs)
 {
-       struct kretprobe_instance *ri;
-
-       if ((ri = get_free_rp_inst(rp)) != NULL) {
-               ri->rp = rp;
-               ri->task = current;
-               ri->ret_addr = (kprobe_opcode_t *)regs->b0;
+       ri->ret_addr = (kprobe_opcode_t *)regs->b0;
 
-               /* Replace the return addr with trampoline addr */
-               regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
-
-               add_rp_inst(ri);
-       } else {
-               rp->nmissed++;
-       }
+       /* Replace the return addr with trampoline addr */
+       regs->b0 = ((struct fnptr *)kretprobe_trampoline)->ip;
 }
 
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
@@ -427,6 +485,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        unsigned long kprobe_inst=0;
        unsigned int slot = addr & 0xf, template, major_opcode = 0;
        bundle_t *bundle;
+       int qp;
 
        bundle = &((kprobe_opcode_t *)kprobe_addr)->bundle;
        template = bundle->quad0.template;
@@ -441,9 +500,9 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        /* Get kprobe_inst and major_opcode from the bundle */
        get_kprobe_inst(bundle, slot, &kprobe_inst, &major_opcode);
 
-       if (unsupported_inst(template, slot, major_opcode, kprobe_inst, addr))
-                       return -EINVAL;
-
+       qp = unsupported_inst(template, slot, major_opcode, kprobe_inst, addr);
+       if (qp < 0)
+               return -EINVAL;
 
        p->ainsn.insn = get_insn_slot();
        if (!p->ainsn.insn)
@@ -451,30 +510,56 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        memcpy(&p->opcode, kprobe_addr, sizeof(kprobe_opcode_t));
        memcpy(p->ainsn.insn, kprobe_addr, sizeof(kprobe_opcode_t));
 
-       prepare_break_inst(template, slot, major_opcode, kprobe_inst, p);
+       prepare_break_inst(template, slot, major_opcode, kprobe_inst, p, qp);
 
        return 0;
 }
 
 void __kprobes arch_arm_kprobe(struct kprobe *p)
 {
-       unsigned long addr = (unsigned long)p->addr;
-       unsigned long arm_addr = addr & ~0xFULL;
+       unsigned long arm_addr;
+       bundle_t *src, *dest;
+
+       arm_addr = ((unsigned long)p->addr) & ~0xFUL;
+       dest = &((kprobe_opcode_t *)arm_addr)->bundle;
+       src = &p->opcode.bundle;
 
        flush_icache_range((unsigned long)p->ainsn.insn,
                        (unsigned long)p->ainsn.insn + sizeof(kprobe_opcode_t));
-       memcpy((char *)arm_addr, &p->opcode, sizeof(kprobe_opcode_t));
+       switch (p->ainsn.slot) {
+               case 0:
+                       dest->quad0.slot0 = src->quad0.slot0;
+                       break;
+               case 1:
+                       dest->quad1.slot1_p1 = src->quad1.slot1_p1;
+                       break;
+               case 2:
+                       dest->quad1.slot2 = src->quad1.slot2;
+                       break;
+       }
        flush_icache_range(arm_addr, arm_addr + sizeof(kprobe_opcode_t));
 }
 
 void __kprobes arch_disarm_kprobe(struct kprobe *p)
 {
-       unsigned long addr = (unsigned long)p->addr;
-       unsigned long arm_addr = addr & ~0xFULL;
+       unsigned long arm_addr;
+       bundle_t *src, *dest;
 
+       arm_addr = ((unsigned long)p->addr) & ~0xFUL;
+       dest = &((kprobe_opcode_t *)arm_addr)->bundle;
        /* p->ainsn.insn contains the original unaltered kprobe_opcode_t */
-       memcpy((char *) arm_addr, (char *) p->ainsn.insn,
-                                        sizeof(kprobe_opcode_t));
+       src = &p->ainsn.insn->bundle;
+       switch (p->ainsn.slot) {
+               case 0:
+                       dest->quad0.slot0 = src->quad0.slot0;
+                       break;
+               case 1:
+                       dest->quad1.slot1_p1 = src->quad1.slot1_p1;
+                       break;
+               case 2:
+                       dest->quad1.slot2 = src->quad1.slot2;
+                       break;
+       }
        flush_icache_range(arm_addr, arm_addr + sizeof(kprobe_opcode_t));
 }
 
@@ -735,7 +820,7 @@ out:
        return 1;
 }
 
-static int __kprobes kprobes_fault_handler(struct pt_regs *regs, int trapnr)
+int __kprobes kprobes_fault_handler(struct pt_regs *regs, int trapnr)
 {
        struct kprobe *cur = kprobe_running();
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
@@ -807,7 +892,9 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
        switch(val) {
        case DIE_BREAK:
                /* err is break number from ia64_bad_break() */
-               if (args->err == 0x80200 || args->err == 0x80300 || args->err == 0)
+               if ((args->err >> 12) == (__IA64_BREAK_KPROBE >> 12)
+                       || args->err == __IA64_BREAK_JPROBE
+                       || args->err == 0)
                        if (pre_kprobes_handler(args))
                                ret = NOTIFY_STOP;
                break;
@@ -817,13 +904,6 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
                        if (post_kprobes_handler(args->regs))
                                ret = NOTIFY_STOP;
                break;
-       case DIE_PAGE_FAULT:
-               /* kprobe_running() needs smp_processor_id() */
-               preempt_disable();
-               if (kprobe_running() &&
-                       kprobes_fault_handler(args->regs, args->trapnr))
-                       ret = NOTIFY_STOP;
-               preempt_enable();
        default:
                break;
        }
@@ -851,15 +931,20 @@ static void ia64_get_bsp_cfm(struct unw_frame_info *info, void *arg)
                        return;
                }
        } while (unw_unwind(info) >= 0);
-       lp->bsp = 0;
+       lp->bsp = NULL;
        lp->cfm = 0;
        return;
 }
 
+unsigned long arch_deref_entry_point(void *entry)
+{
+       return ((struct fnptr *)entry)->ip;
+}
+
 int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
 {
        struct jprobe *jp = container_of(p, struct jprobe, kp);
-       unsigned long addr = ((struct fnptr *)(jp->entry))->ip;
+       unsigned long addr = arch_deref_entry_point(jp->entry);
        struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
        struct param_bsp_cfm pa;
        int bytes;
@@ -867,7 +952,7 @@ int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
        /*
         * Callee owns the argument space and could overwrite it, eg
         * tail call optimization. So to be absolutely safe
-        * we save the argument space before transfering the control
+        * we save the argument space before transferring the control
         * to instrumented jprobe function which runs in
         * the process context
         */
@@ -929,3 +1014,12 @@ int __init arch_init_kprobes(void)
                (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip;
        return register_kprobe(&trampoline_p);
 }
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+       if (p->addr ==
+               (kprobe_opcode_t *)((struct fnptr *)kretprobe_trampoline)->ip)
+               return 1;
+
+       return 0;
+}
This page took 0.055406 seconds and 5 git commands to generate.