Skip to content

Commit c28f896

Browse files
Prasanna S PanchamukhiLinus Torvalds
authored andcommitted
[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 <[email protected]> Cc: Andi Kleen <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent b402651 commit c28f896

File tree

1 file changed

+55
-7
lines changed

1 file changed

+55
-7
lines changed

arch/x86_64/kernel/kprobes.c

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,12 @@
3737
#include <linux/string.h>
3838
#include <linux/slab.h>
3939
#include <linux/preempt.h>
40+
#include <linux/module.h>
4041

4142
#include <asm/cacheflush.h>
4243
#include <asm/pgtable.h>
4344
#include <asm/kdebug.h>
45+
#include <asm/uaccess.h>
4446

4547
void jprobe_return_end(void);
4648
static void __kprobes arch_copy_kprobe(struct kprobe *p);
@@ -578,16 +580,62 @@ int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
578580
{
579581
struct kprobe *cur = kprobe_running();
580582
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
583+
const struct exception_table_entry *fixup;
581584

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;
587596
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();
590601
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;
591639
}
592640
return 0;
593641
}

0 commit comments

Comments
 (0)