diff options
Diffstat (limited to 'arch/i386/kernel/kprobes.c')
-rw-r--r-- | arch/i386/kernel/kprobes.c | 57 |
1 files changed, 50 insertions, 7 deletions
diff --git a/arch/i386/kernel/kprobes.c b/arch/i386/kernel/kprobes.c index df1b346d36ff..f19768789e8a 100644 --- a/arch/i386/kernel/kprobes.c +++ b/arch/i386/kernel/kprobes.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <asm/cacheflush.h> | 35 | #include <asm/cacheflush.h> |
36 | #include <asm/kdebug.h> | 36 | #include <asm/kdebug.h> |
37 | #include <asm/desc.h> | 37 | #include <asm/desc.h> |
38 | #include <asm/uaccess.h> | ||
38 | 39 | ||
39 | void jprobe_return_end(void); | 40 | void jprobe_return_end(void); |
40 | 41 | ||
@@ -547,15 +548,57 @@ static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr) | |||
547 | struct kprobe *cur = kprobe_running(); | 548 | struct kprobe *cur = kprobe_running(); |
548 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 549 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
549 | 550 | ||
550 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | 551 | switch(kcb->kprobe_status) { |
551 | return 1; | 552 | case KPROBE_HIT_SS: |
552 | 553 | case KPROBE_REENTER: | |
553 | if (kcb->kprobe_status & KPROBE_HIT_SS) { | 554 | /* |
554 | resume_execution(cur, regs, kcb); | 555 | * We are here because the instruction being single |
556 | * stepped caused a page fault. We reset the current | ||
557 | * kprobe and the eip points back to the probe address | ||
558 | * and allow the page fault handler to continue as a | ||
559 | * normal page fault. | ||
560 | */ | ||
561 | regs->eip = (unsigned long)cur->addr; | ||
555 | regs->eflags |= kcb->kprobe_old_eflags; | 562 | regs->eflags |= kcb->kprobe_old_eflags; |
556 | 563 | if (kcb->kprobe_status == KPROBE_REENTER) | |
557 | reset_current_kprobe(); | 564 | restore_previous_kprobe(kcb); |
565 | else | ||
566 | reset_current_kprobe(); | ||
558 | preempt_enable_no_resched(); | 567 | preempt_enable_no_resched(); |
568 | break; | ||
569 | case KPROBE_HIT_ACTIVE: | ||
570 | case KPROBE_HIT_SSDONE: | ||
571 | /* | ||
572 | * We increment the nmissed count for accounting, | ||
573 | * we can also use npre/npostfault count for accouting | ||
574 | * these specific fault cases. | ||
575 | */ | ||
576 | kprobes_inc_nmissed_count(cur); | ||
577 | |||
578 | /* | ||
579 | * We come here because instructions in the pre/post | ||
580 | * handler caused the page_fault, this could happen | ||
581 | * if handler tries to access user space by | ||
582 | * copy_from_user(), get_user() etc. Let the | ||
583 | * user-specified handler try to fix it first. | ||
584 | */ | ||
585 | if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) | ||
586 | return 1; | ||
587 | |||
588 | /* | ||
589 | * In case the user-specified fault handler returned | ||
590 | * zero, try to fix up. | ||
591 | */ | ||
592 | if (fixup_exception(regs)) | ||
593 | return 1; | ||
594 | |||
595 | /* | ||
596 | * fixup_exception() could not handle it, | ||
597 | * Let do_page_fault() fix it. | ||
598 | */ | ||
599 | break; | ||
600 | default: | ||
601 | break; | ||
559 | } | 602 | } |
560 | return 0; | 603 | return 0; |
561 | } | 604 | } |