diff options
Diffstat (limited to 'arch/x86_64')
-rw-r--r-- | arch/x86_64/kernel/kprobes.c | 62 |
1 files changed, 55 insertions, 7 deletions
diff --git a/arch/x86_64/kernel/kprobes.c b/arch/x86_64/kernel/kprobes.c index 218e015c3195..accbff3fec49 100644 --- a/arch/x86_64/kernel/kprobes.c +++ b/arch/x86_64/kernel/kprobes.c | |||
@@ -37,10 +37,12 @@ | |||
37 | #include <linux/string.h> | 37 | #include <linux/string.h> |
38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
39 | #include <linux/preempt.h> | 39 | #include <linux/preempt.h> |
40 | #include <linux/module.h> | ||
40 | 41 | ||
41 | #include <asm/cacheflush.h> | 42 | #include <asm/cacheflush.h> |
42 | #include <asm/pgtable.h> | 43 | #include <asm/pgtable.h> |
43 | #include <asm/kdebug.h> | 44 | #include <asm/kdebug.h> |
45 | #include <asm/uaccess.h> | ||
44 | 46 | ||
45 | void jprobe_return_end(void); | 47 | void jprobe_return_end(void); |
46 | static void __kprobes arch_copy_kprobe(struct kprobe *p); | 48 | static void __kprobes arch_copy_kprobe(struct kprobe *p); |
@@ -578,16 +580,62 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr) | |||
578 | { | 580 | { |
579 | struct kprobe *cur = kprobe_running(); | 581 | struct kprobe *cur = kprobe_running(); |
580 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 582 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
583 | const struct exception_table_entry *fixup; | ||
581 | 584 | ||
582 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | 585 | switch(kcb->kprobe_status) { |
583 | return 1; | 586 | case KPROBE_HIT_SS: |
584 | 587 | case KPROBE_REENTER: | |
585 | if (kcb->kprobe_status & KPROBE_HIT_SS) { | 588 | /* |
586 | resume_execution(cur, regs, kcb); | 589 | * We are here because the instruction being single |
590 | * stepped caused a page fault. We reset the current | ||
591 | * kprobe and the rip points back to the probe address | ||
592 | * and allow the page fault handler to continue as a | ||
593 | * normal page fault. | ||
594 | */ | ||
595 | regs->rip = (unsigned long)cur->addr; | ||
587 | regs->eflags |= kcb->kprobe_old_rflags; | 596 | regs->eflags |= kcb->kprobe_old_rflags; |
588 | 597 | if (kcb->kprobe_status == KPROBE_REENTER) | |
589 | reset_current_kprobe(); | 598 | restore_previous_kprobe(kcb); |
599 | else | ||
600 | reset_current_kprobe(); | ||
590 | preempt_enable_no_resched(); | 601 | preempt_enable_no_resched(); |
602 | break; | ||
603 | case KPROBE_HIT_ACTIVE: | ||
604 | case KPROBE_HIT_SSDONE: | ||
605 | /* | ||
606 | * We increment the nmissed count for accounting, | ||
607 | * we can also use npre/npostfault count for accouting | ||
608 | * these specific fault cases. | ||
609 | */ | ||
610 | kprobes_inc_nmissed_count(cur); | ||
611 | |||
612 | /* | ||
613 | * We come here because instructions in the pre/post | ||
614 | * handler caused the page_fault, this could happen | ||
615 | * if handler tries to access user space by | ||
616 | * copy_from_user(), get_user() etc. Let the | ||
617 | * user-specified handler try to fix it first. | ||
618 | */ | ||
619 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | ||
620 | return 1; | ||
621 | |||
622 | /* | ||
623 | * In case the user-specified fault handler returned | ||
624 | * zero, try to fix up. | ||
625 | */ | ||
626 | fixup = search_exception_tables(regs->rip); | ||
627 | if (fixup) { | ||
628 | regs->rip = fixup->fixup; | ||
629 | return 1; | ||
630 | } | ||
631 | |||
632 | /* | ||
633 | * fixup() could not handle it, | ||
634 | * Let do_page_fault() fix it. | ||
635 | */ | ||
636 | break; | ||
637 | default: | ||
638 | break; | ||
591 | } | 639 | } |
592 | return 0; | 640 | return 0; |
593 | } | 641 | } |