aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPrasanna S Panchamukhi <prasanna@in.ibm.com>2006-03-26 04:38:24 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-26 11:57:04 -0500
commit50e21f2b2861711b6df0b72ffef4a70d1532b023 (patch)
treeec25f24103f10a6a620682fc9e1ab0849c7b4dce
parentc28f896634f2c931a298490deab3861ab117716a (diff)
[PATCH] kprobes: fix broken fault handling for powerpc64
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: Paul Mackerras <paulus@samba.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/powerpc/kernel/kprobes.c63
1 files changed, 55 insertions, 8 deletions
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c
index aea25dd18dae..ad7a90212204 100644
--- a/arch/powerpc/kernel/kprobes.c
+++ b/arch/powerpc/kernel/kprobes.c
@@ -30,9 +30,11 @@
30#include <linux/kprobes.h> 30#include <linux/kprobes.h>
31#include <linux/ptrace.h> 31#include <linux/ptrace.h>
32#include <linux/preempt.h> 32#include <linux/preempt.h>
33#include <linux/module.h>
33#include <asm/cacheflush.h> 34#include <asm/cacheflush.h>
34#include <asm/kdebug.h> 35#include <asm/kdebug.h>
35#include <asm/sstep.h> 36#include <asm/sstep.h>
37#include <asm/uaccess.h>
36 38
37DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL; 39DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
38DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk); 40DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
@@ -372,17 +374,62 @@ static inline int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
372{ 374{
373 struct kprobe *cur = kprobe_running(); 375 struct kprobe *cur = kprobe_running();
374 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); 376 struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
375 377 const struct exception_table_entry *entry;
376 if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr)) 378
377 return 1; 379 switch(kcb->kprobe_status) {
378 380 case KPROBE_HIT_SS:
379 if (kcb->kprobe_status & KPROBE_HIT_SS) { 381 case KPROBE_REENTER:
380 resume_execution(cur, regs); 382 /*
383 * We are here because the instruction being single
384 * stepped caused a page fault. We reset the current
385 * kprobe and the nip points back to the probe address
386 * and allow the page fault handler to continue as a
387 * normal page fault.
388 */
389 regs->nip = (unsigned long)cur->addr;
381 regs->msr &= ~MSR_SE; 390 regs->msr &= ~MSR_SE;
382 regs->msr |= kcb->kprobe_saved_msr; 391 regs->msr |= kcb->kprobe_saved_msr;
383 392 if (kcb->kprobe_status == KPROBE_REENTER)
384 reset_current_kprobe(); 393 restore_previous_kprobe(kcb);
394 else
395 reset_current_kprobe();
385 preempt_enable_no_resched(); 396 preempt_enable_no_resched();
397 break;
398 case KPROBE_HIT_ACTIVE:
399 case KPROBE_HIT_SSDONE:
400 /*
401 * We increment the nmissed count for accounting,
402 * we can also use npre/npostfault count for accouting
403 * these specific fault cases.
404 */
405 kprobes_inc_nmissed_count(cur);
406
407 /*
408 * We come here because instructions in the pre/post
409 * handler caused the page_fault, this could happen
410 * if handler tries to access user space by
411 * copy_from_user(), get_user() etc. Let the
412 * user-specified handler try to fix it first.
413 */
414 if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
415 return 1;
416
417 /*
418 * In case the user-specified fault handler returned
419 * zero, try to fix up.
420 */
421 if ((entry = search_exception_tables(regs->nip)) != NULL) {
422 regs->nip = entry->fixup;
423 return 1;
424 }
425
426 /*
427 * fixup_exception() could not handle it,
428 * Let do_page_fault() fix it.
429 */
430 break;
431 default:
432 break;
386 } 433 }
387 return 0; 434 return 0;
388} 435}