Skip to content

Commit 237d28d

Browse files
committed
ftrace/jprobes/x86: Fix conflict between jprobes and function graph tracing
If the function graph tracer traces a jprobe callback, the system will crash. This can easily be demonstrated by compiling the jprobe sample module that is in the kernel tree, loading it and running the function graph tracer. # modprobe jprobe_example.ko # echo function_graph > /sys/kernel/debug/tracing/current_tracer # ls The first two commands end up in a nice crash after the first fork. (do_fork has a jprobe attached to it, so "ls" just triggers that fork) The problem is caused by the jprobe_return() that all jprobe callbacks must end with. The way jprobes works is that the function a jprobe is attached to has a breakpoint placed at the start of it (or it uses ftrace if fentry is supported). The breakpoint handler (or ftrace callback) will copy the stack frame and change the ip address to return to the jprobe handler instead of the function. The jprobe handler must end with jprobe_return() which swaps the stack and does an int3 (breakpoint). This breakpoint handler will then put back the saved stack frame, simulate the instruction at the beginning of the function it added a breakpoint to, and then continue on. For function tracing to work, it hijakes the return address from the stack frame, and replaces it with a hook function that will trace the end of the call. This hook function will restore the return address of the function call. If the function tracer traces the jprobe handler, the hook function for that handler will not be called, and its saved return address will be used for the next function. This will result in a kernel crash. To solve this, pause function tracing before the jprobe handler is called and unpause it before it returns back to the function it probed. Some other updates: Used a variable "saved_sp" to hold kcb->jprobe_saved_sp. This makes the code look a bit cleaner and easier to understand (various tries to fix this bug required this change). Note, if fentry is being used, jprobes will change the ip address before the function graph tracer runs and it will not be able to trace the function that the jprobe is probing. Link: http://lkml.kernel.org/r/[email protected] Cc: [email protected] # 2.6.30+ Acked-by: Masami Hiramatsu <[email protected]> Signed-off-by: Steven Rostedt <[email protected]>
1 parent 7485058 commit 237d28d

File tree

1 file changed

+15
-5
lines changed

1 file changed

+15
-5
lines changed

arch/x86/kernel/kprobes/core.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,15 @@ int setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
10201020
regs->flags &= ~X86_EFLAGS_IF;
10211021
trace_hardirqs_off();
10221022
regs->ip = (unsigned long)(jp->entry);
1023+
1024+
/*
1025+
* jprobes use jprobe_return() which skips the normal return
1026+
* path of the function, and this messes up the accounting of the
1027+
* function graph tracer to get messed up.
1028+
*
1029+
* Pause function graph tracing while performing the jprobe function.
1030+
*/
1031+
pause_graph_tracing();
10231032
return 1;
10241033
}
10251034
NOKPROBE_SYMBOL(setjmp_pre_handler);
@@ -1048,24 +1057,25 @@ int longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
10481057
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
10491058
u8 *addr = (u8 *) (regs->ip - 1);
10501059
struct jprobe *jp = container_of(p, struct jprobe, kp);
1060+
void *saved_sp = kcb->jprobe_saved_sp;
10511061

10521062
if ((addr > (u8 *) jprobe_return) &&
10531063
(addr < (u8 *) jprobe_return_end)) {
1054-
if (stack_addr(regs) != kcb->jprobe_saved_sp) {
1064+
if (stack_addr(regs) != saved_sp) {
10551065
struct pt_regs *saved_regs = &kcb->jprobe_saved_regs;
10561066
printk(KERN_ERR
10571067
"current sp %p does not match saved sp %p\n",
1058-
stack_addr(regs), kcb->jprobe_saved_sp);
1068+
stack_addr(regs), saved_sp);
10591069
printk(KERN_ERR "Saved registers for jprobe %p\n", jp);
10601070
show_regs(saved_regs);
10611071
printk(KERN_ERR "Current registers\n");
10621072
show_regs(regs);
10631073
BUG();
10641074
}
1075+
/* It's OK to start function graph tracing again */
1076+
unpause_graph_tracing();
10651077
*regs = kcb->jprobe_saved_regs;
1066-
memcpy((kprobe_opcode_t *)(kcb->jprobe_saved_sp),
1067-
kcb->jprobes_stack,
1068-
MIN_STACK_SIZE(kcb->jprobe_saved_sp));
1078+
memcpy(saved_sp, kcb->jprobes_stack, MIN_STACK_SIZE(saved_sp));
10691079
preempt_enable_no_resched();
10701080
return 1;
10711081
}

0 commit comments

Comments
 (0)