]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - arch/x86/kernel/kprobes.c
kprobes/x86: Cleanup save/restore registers
[net-next-2.6.git] / arch / x86 / kernel / kprobes.c
index 5b8c7505b3bc2438429a330117a36c20dcf16339..4ae95befd0eb584cbbe2408fd2e4f83f35e2826d 100644 (file)
@@ -115,7 +115,7 @@ static void __kprobes set_jmp_op(void *from, void *to)
        } __attribute__((packed)) * jop;
        jop = (struct __arch_jmp_op *)from;
        jop->raddr = (s32)((long)(to) - ((long)(from) + 5));
-       jop->op = RELATIVEJUMP_INSTRUCTION;
+       jop->op = RELATIVEJUMP_OPCODE;
 }
 
 /*
@@ -337,6 +337,9 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p)
 
 int __kprobes arch_prepare_kprobe(struct kprobe *p)
 {
+       if (alternatives_text_reserved(p->addr, p->addr))
+               return -EINVAL;
+
        if (!can_probe((unsigned long)p->addr))
                return -EILSEQ;
        /* insn: must be on special executable page on x86. */
@@ -403,18 +406,6 @@ static void __kprobes restore_btf(void)
                update_debugctlmsr(current->thread.debugctlmsr);
 }
 
-static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
-{
-       clear_btf();
-       regs->flags |= X86_EFLAGS_TF;
-       regs->flags &= ~X86_EFLAGS_IF;
-       /* single step inline if the instruction is an int3 */
-       if (p->opcode == BREAKPOINT_INSTRUCTION)
-               regs->ip = (unsigned long)p->addr;
-       else
-               regs->ip = (unsigned long)p->ainsn.insn;
-}
-
 void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
                                      struct pt_regs *regs)
 {
@@ -427,19 +418,38 @@ void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
 }
 
 static void __kprobes setup_singlestep(struct kprobe *p, struct pt_regs *regs,
-                                      struct kprobe_ctlblk *kcb)
+                                      struct kprobe_ctlblk *kcb, int reenter)
 {
-#if !defined(CONFIG_PREEMPT) || defined(CONFIG_FREEZER)
+#if !defined(CONFIG_PREEMPT)
        if (p->ainsn.boostable == 1 && !p->post_handler) {
                /* Boost up -- we can execute copied instructions directly */
-               reset_current_kprobe();
+               if (!reenter)
+                       reset_current_kprobe();
+               /*
+                * Reentering boosted probe doesn't reset current_kprobe,
+                * nor set current_kprobe, because it doesn't use single
+                * stepping.
+                */
                regs->ip = (unsigned long)p->ainsn.insn;
                preempt_enable_no_resched();
                return;
        }
 #endif
-       prepare_singlestep(p, regs);
-       kcb->kprobe_status = KPROBE_HIT_SS;
+       if (reenter) {
+               save_previous_kprobe(kcb);
+               set_current_kprobe(p, regs, kcb);
+               kcb->kprobe_status = KPROBE_REENTER;
+       } else
+               kcb->kprobe_status = KPROBE_HIT_SS;
+       /* Prepare real single stepping */
+       clear_btf();
+       regs->flags |= X86_EFLAGS_TF;
+       regs->flags &= ~X86_EFLAGS_IF;
+       /* single step inline if the instruction is an int3 */
+       if (p->opcode == BREAKPOINT_INSTRUCTION)
+               regs->ip = (unsigned long)p->addr;
+       else
+               regs->ip = (unsigned long)p->ainsn.insn;
 }
 
 /*
@@ -453,11 +463,8 @@ static int __kprobes reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
        switch (kcb->kprobe_status) {
        case KPROBE_HIT_SSDONE:
        case KPROBE_HIT_ACTIVE:
-               save_previous_kprobe(kcb);
-               set_current_kprobe(p, regs, kcb);
                kprobes_inc_nmissed_count(p);
-               prepare_singlestep(p, regs);
-               kcb->kprobe_status = KPROBE_REENTER;
+               setup_singlestep(p, regs, kcb, 1);
                break;
        case KPROBE_HIT_SS:
                /* A probe has been hit in the codepath leading up to, or just
@@ -532,13 +539,13 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
                         * more here.
                         */
                        if (!p->pre_handler || !p->pre_handler(p, regs))
-                               setup_singlestep(p, regs, kcb);
+                               setup_singlestep(p, regs, kcb, 0);
                        return 1;
                }
        } else if (kprobe_running()) {
                p = __get_cpu_var(current_kprobe);
                if (p->break_handler && p->break_handler(p, regs)) {
-                       setup_singlestep(p, regs, kcb);
+                       setup_singlestep(p, regs, kcb, 0);
                        return 1;
                }
        } /* else: not a kprobe fault; let the kernel handle it */
@@ -547,6 +554,69 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
        return 0;
 }
 
+#ifdef CONFIG_X86_64
+#define SAVE_REGS_STRING               \
+       /* Skip cs, ip, orig_ax. */     \
+       "       subq $24, %rsp\n"       \
+       "       pushq %rdi\n"           \
+       "       pushq %rsi\n"           \
+       "       pushq %rdx\n"           \
+       "       pushq %rcx\n"           \
+       "       pushq %rax\n"           \
+       "       pushq %r8\n"            \
+       "       pushq %r9\n"            \
+       "       pushq %r10\n"           \
+       "       pushq %r11\n"           \
+       "       pushq %rbx\n"           \
+       "       pushq %rbp\n"           \
+       "       pushq %r12\n"           \
+       "       pushq %r13\n"           \
+       "       pushq %r14\n"           \
+       "       pushq %r15\n"
+#define RESTORE_REGS_STRING            \
+       "       popq %r15\n"            \
+       "       popq %r14\n"            \
+       "       popq %r13\n"            \
+       "       popq %r12\n"            \
+       "       popq %rbp\n"            \
+       "       popq %rbx\n"            \
+       "       popq %r11\n"            \
+       "       popq %r10\n"            \
+       "       popq %r9\n"             \
+       "       popq %r8\n"             \
+       "       popq %rax\n"            \
+       "       popq %rcx\n"            \
+       "       popq %rdx\n"            \
+       "       popq %rsi\n"            \
+       "       popq %rdi\n"            \
+       /* Skip orig_ax, ip, cs */      \
+       "       addq $24, %rsp\n"
+#else
+#define SAVE_REGS_STRING               \
+       /* Skip cs, ip, orig_ax and gs. */      \
+       "       subl $16, %esp\n"       \
+       "       pushl %fs\n"            \
+       "       pushl %ds\n"            \
+       "       pushl %es\n"            \
+       "       pushl %eax\n"           \
+       "       pushl %ebp\n"           \
+       "       pushl %edi\n"           \
+       "       pushl %esi\n"           \
+       "       pushl %edx\n"           \
+       "       pushl %ecx\n"           \
+       "       pushl %ebx\n"
+#define RESTORE_REGS_STRING            \
+       "       popl %ebx\n"            \
+       "       popl %ecx\n"            \
+       "       popl %edx\n"            \
+       "       popl %esi\n"            \
+       "       popl %edi\n"            \
+       "       popl %ebp\n"            \
+       "       popl %eax\n"            \
+       /* Skip ds, es, fs, gs, orig_ax, and ip. Note: don't pop cs here*/\
+       "       addl $24, %esp\n"
+#endif
+
 /*
  * When a retprobed function returns, this code saves registers and
  * calls trampoline_handler() runs, which calls the kretprobe's handler.
@@ -560,65 +630,16 @@ static void __used __kprobes kretprobe_trampoline_holder(void)
                        /* We don't bother saving the ss register */
                        "       pushq %rsp\n"
                        "       pushfq\n"
-                       /*
-                        * Skip cs, ip, orig_ax.
-                        * trampoline_handler() will plug in these values
-                        */
-                       "       subq $24, %rsp\n"
-                       "       pushq %rdi\n"
-                       "       pushq %rsi\n"
-                       "       pushq %rdx\n"
-                       "       pushq %rcx\n"
-                       "       pushq %rax\n"
-                       "       pushq %r8\n"
-                       "       pushq %r9\n"
-                       "       pushq %r10\n"
-                       "       pushq %r11\n"
-                       "       pushq %rbx\n"
-                       "       pushq %rbp\n"
-                       "       pushq %r12\n"
-                       "       pushq %r13\n"
-                       "       pushq %r14\n"
-                       "       pushq %r15\n"
+                       SAVE_REGS_STRING
                        "       movq %rsp, %rdi\n"
                        "       call trampoline_handler\n"
                        /* Replace saved sp with true return address. */
                        "       movq %rax, 152(%rsp)\n"
-                       "       popq %r15\n"
-                       "       popq %r14\n"
-                       "       popq %r13\n"
-                       "       popq %r12\n"
-                       "       popq %rbp\n"
-                       "       popq %rbx\n"
-                       "       popq %r11\n"
-                       "       popq %r10\n"
-                       "       popq %r9\n"
-                       "       popq %r8\n"
-                       "       popq %rax\n"
-                       "       popq %rcx\n"
-                       "       popq %rdx\n"
-                       "       popq %rsi\n"
-                       "       popq %rdi\n"
-                       /* Skip orig_ax, ip, cs */
-                       "       addq $24, %rsp\n"
+                       RESTORE_REGS_STRING
                        "       popfq\n"
 #else
                        "       pushf\n"
-                       /*
-                        * Skip cs, ip, orig_ax and gs.
-                        * trampoline_handler() will plug in these values
-                        */
-                       "       subl $16, %esp\n"
-                       "       pushl %fs\n"
-                       "       pushl %es\n"
-                       "       pushl %ds\n"
-                       "       pushl %eax\n"
-                       "       pushl %ebp\n"
-                       "       pushl %edi\n"
-                       "       pushl %esi\n"
-                       "       pushl %edx\n"
-                       "       pushl %ecx\n"
-                       "       pushl %ebx\n"
+                       SAVE_REGS_STRING
                        "       movl %esp, %eax\n"
                        "       call trampoline_handler\n"
                        /* Move flags to cs */
@@ -626,15 +647,7 @@ static void __used __kprobes kretprobe_trampoline_holder(void)
                        "       movl %edx, 52(%esp)\n"
                        /* Replace saved flags with true return address. */
                        "       movl %eax, 56(%esp)\n"
-                       "       popl %ebx\n"
-                       "       popl %ecx\n"
-                       "       popl %edx\n"
-                       "       popl %esi\n"
-                       "       popl %edi\n"
-                       "       popl %ebp\n"
-                       "       popl %eax\n"
-                       /* Skip ds, es, fs, gs, orig_ax and ip */
-                       "       addl $24, %esp\n"
+                       RESTORE_REGS_STRING
                        "       popf\n"
 #endif
                        "       ret\n");