aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86_64/kernel
diff options
context:
space:
mode:
authorPrasanna S Panchamukhi <prasanna@in.ibm.com>2006-03-26 04:38:23 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-26 11:57:04 -0500
commitc28f896634f2c931a298490deab3861ab117716a (patch)
treed68019b13f2eacc546fff176686bee0d99ab05df /arch/x86_64/kernel
parentb4026513b88e7eaa52a31117e2b7bafdc1e40ef1 (diff)
[PATCH] kprobes: fix broken fault handling for x86_64
Provide proper kprobes fault handling, if a user-specified pre/post handlers tries to access user address space, through copy_from_user(), get_user() etc. The user-specified fault handler gets called only if the fault occurs while executing user-specified handlers. In such a case user-specified handler is allowed to fix it first, later if the user-specifed fault handler does not fix it, we try to fix it by calling fix_exception(). The user-specified handler will not be called if the fault happens when single stepping the original instruction, instead we reset the current probe and allow the system page fault handler to fix it up. Signed-off-by: Prasanna S Panchamukhi <prasanna@in.ibm.com> Cc: Andi Kleen <ak@muc.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/x86_64/kernel')
-rw-r--r--arch/x86_64/kernel/kprobes.c62
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
45void jprobe_return_end(void); 47void jprobe_return_end(void);
46static void __kprobes arch_copy_kprobe(struct kprobe *p); 48static 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}