aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorPrasanna S Panchamukhi <prasanna@in.ibm.com>2006-03-26 04:38:22 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-26 11:57:04 -0500
commitb4026513b88e7eaa52a31117e2b7bafdc1e40ef1 (patch)
tree92bbffa53339d237424099bce8fb72d8996f93c2 /arch
parent2326c77017c79fd6d55c69d8a49a57a252921bcd (diff)
[PATCH] kprobes: fix broken fault handling for i386
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> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/i386/kernel/kprobes.c57
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
39void jprobe_return_end(void); 40void 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}