|
37 | 37 | #include <linux/string.h>
|
38 | 38 | #include <linux/slab.h>
|
39 | 39 | #include <linux/preempt.h>
|
| 40 | +#include <linux/module.h> |
40 | 41 |
|
41 | 42 | #include <asm/cacheflush.h>
|
42 | 43 | #include <asm/pgtable.h>
|
43 | 44 | #include <asm/kdebug.h>
|
| 45 | +#include <asm/uaccess.h> |
44 | 46 |
|
45 | 47 | void jprobe_return_end(void);
|
46 | 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 | 581 | struct kprobe *cur = kprobe_running();
|
580 | 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)) |
583 |
| - return 1; |
584 |
| - |
585 |
| - if (kcb->kprobe_status & KPROBE_HIT_SS) { |
586 |
| - resume_execution(cur, regs, kcb); |
| 585 | + switch(kcb->kprobe_status) { |
| 586 | + case KPROBE_HIT_SS: |
| 587 | + case KPROBE_REENTER: |
| 588 | + /* |
| 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 | 596 | regs->eflags |= kcb->kprobe_old_rflags;
|
588 |
| - |
589 |
| - reset_current_kprobe(); |
| 597 | + if (kcb->kprobe_status == KPROBE_REENTER) |
| 598 | + restore_previous_kprobe(kcb); |
| 599 | + else |
| 600 | + reset_current_kprobe(); |
590 | 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 | 640 | return 0;
|
593 | 641 | }
|
|
0 commit comments