mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 00:48:50 +00:00
dc912c3035
Fengguang Wu's zero day bot triggered a stack unwinder dump. This can
be easily triggered when CONFIG_FRAME_POINTERS is enabled and -mfentry
is in use on x86_32.
># cd /sys/kernel/debug/tracing
># echo 'p:schedule schedule' > kprobe_events
># echo stacktrace > events/kprobes/schedule/trigger
This is because the code that implemented fentry in the ftrace_regs_caller
tried to use the least amount of #ifdefs, and modified ebp when
CC_USE_FENTRY was defined to point to the parent ip as it does when
CC_USE_FENTRY is not defined. But when CONFIG_FRAME_POINTERS is set, it
corrupts the ebp register for this frame while doing the tracing.
NOTE, it does not corrupt ebp in any other way. It is just a bad frame
pointer when calling into the tracing infrastructure. The original ebp is
restored before returning from the fentry call. But if a stack trace is
performed inside the tracing, the unwinder will notice the bad ebp.
Instead of toying with ebp with CC_USING_FENTRY, just slap the parent ip
into the second parameter (%edx), and have an #else that does it the
original way.
The unwinder will unfortunately miss the function being traced, as the
stack frame is not set up yet for it, as it is for x86_64. But fixing that
is a bit more complex and did not work before anyway.
This has been tested with and without FRAME_POINTERS being set while using
-mfentry, as well as using an older compiler that uses mcount.
Analyzed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Fixes: 644e0e8dc7
("x86/ftrace: Add -mfentry support to x86_32 with DYNAMIC_FTRACE set")
Reported-by: kernel test robot <fengguang.wu@intel.com>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Link: https://lists.01.org/pipermail/lkp/2017-April/006165.html
Link: http://lkml.kernel.org/r/20170420172236.7af7f6e5@gandalf.local.home
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
244 lines
4.9 KiB
ArmAsm
244 lines
4.9 KiB
ArmAsm
/*
|
|
* Copyright (C) 2017 Steven Rostedt, VMware Inc.
|
|
*/
|
|
|
|
#include <linux/linkage.h>
|
|
#include <asm/page_types.h>
|
|
#include <asm/segment.h>
|
|
#include <asm/export.h>
|
|
#include <asm/ftrace.h>
|
|
|
|
#ifdef CC_USING_FENTRY
|
|
# define function_hook __fentry__
|
|
EXPORT_SYMBOL(__fentry__)
|
|
#else
|
|
# define function_hook mcount
|
|
EXPORT_SYMBOL(mcount)
|
|
#endif
|
|
|
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
|
|
|
/* mcount uses a frame pointer even if CONFIG_FRAME_POINTER is not set */
|
|
#if !defined(CC_USING_FENTRY) || defined(CONFIG_FRAME_POINTER)
|
|
# define USING_FRAME_POINTER
|
|
#endif
|
|
|
|
#ifdef USING_FRAME_POINTER
|
|
# define MCOUNT_FRAME 1 /* using frame = true */
|
|
#else
|
|
# define MCOUNT_FRAME 0 /* using frame = false */
|
|
#endif
|
|
|
|
ENTRY(function_hook)
|
|
ret
|
|
END(function_hook)
|
|
|
|
ENTRY(ftrace_caller)
|
|
|
|
#ifdef USING_FRAME_POINTER
|
|
# ifdef CC_USING_FENTRY
|
|
/*
|
|
* Frame pointers are of ip followed by bp.
|
|
* Since fentry is an immediate jump, we are left with
|
|
* parent-ip, function-ip. We need to add a frame with
|
|
* parent-ip followed by ebp.
|
|
*/
|
|
pushl 4(%esp) /* parent ip */
|
|
pushl %ebp
|
|
movl %esp, %ebp
|
|
pushl 2*4(%esp) /* function ip */
|
|
# endif
|
|
/* For mcount, the function ip is directly above */
|
|
pushl %ebp
|
|
movl %esp, %ebp
|
|
#endif
|
|
pushl %eax
|
|
pushl %ecx
|
|
pushl %edx
|
|
pushl $0 /* Pass NULL as regs pointer */
|
|
|
|
#ifdef USING_FRAME_POINTER
|
|
/* Load parent ebp into edx */
|
|
movl 4*4(%esp), %edx
|
|
#else
|
|
/* There's no frame pointer, load the appropriate stack addr instead */
|
|
lea 4*4(%esp), %edx
|
|
#endif
|
|
|
|
movl (MCOUNT_FRAME+4)*4(%esp), %eax /* load the rip */
|
|
/* Get the parent ip */
|
|
movl 4(%edx), %edx /* edx has ebp */
|
|
|
|
movl function_trace_op, %ecx
|
|
subl $MCOUNT_INSN_SIZE, %eax
|
|
|
|
.globl ftrace_call
|
|
ftrace_call:
|
|
call ftrace_stub
|
|
|
|
addl $4, %esp /* skip NULL pointer */
|
|
popl %edx
|
|
popl %ecx
|
|
popl %eax
|
|
#ifdef USING_FRAME_POINTER
|
|
popl %ebp
|
|
# ifdef CC_USING_FENTRY
|
|
addl $4,%esp /* skip function ip */
|
|
popl %ebp /* this is the orig bp */
|
|
addl $4, %esp /* skip parent ip */
|
|
# endif
|
|
#endif
|
|
.Lftrace_ret:
|
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
.globl ftrace_graph_call
|
|
ftrace_graph_call:
|
|
jmp ftrace_stub
|
|
#endif
|
|
|
|
/* This is weak to keep gas from relaxing the jumps */
|
|
WEAK(ftrace_stub)
|
|
ret
|
|
END(ftrace_caller)
|
|
|
|
ENTRY(ftrace_regs_caller)
|
|
/*
|
|
* i386 does not save SS and ESP when coming from kernel.
|
|
* Instead, to get sp, ®s->sp is used (see ptrace.h).
|
|
* Unfortunately, that means eflags must be at the same location
|
|
* as the current return ip is. We move the return ip into the
|
|
* regs->ip location, and move flags into the return ip location.
|
|
*/
|
|
pushl $__KERNEL_CS
|
|
pushl 4(%esp) /* Save the return ip */
|
|
pushl $0 /* Load 0 into orig_ax */
|
|
pushl %gs
|
|
pushl %fs
|
|
pushl %es
|
|
pushl %ds
|
|
pushl %eax
|
|
|
|
/* Get flags and place them into the return ip slot */
|
|
pushf
|
|
popl %eax
|
|
movl %eax, 8*4(%esp)
|
|
|
|
pushl %ebp
|
|
pushl %edi
|
|
pushl %esi
|
|
pushl %edx
|
|
pushl %ecx
|
|
pushl %ebx
|
|
|
|
movl 12*4(%esp), %eax /* Load ip (1st parameter) */
|
|
subl $MCOUNT_INSN_SIZE, %eax /* Adjust ip */
|
|
#ifdef CC_USING_FENTRY
|
|
movl 15*4(%esp), %edx /* Load parent ip (2nd parameter) */
|
|
#else
|
|
movl 0x4(%ebp), %edx /* Load parent ip (2nd parameter) */
|
|
#endif
|
|
movl function_trace_op, %ecx /* Save ftrace_pos in 3rd parameter */
|
|
pushl %esp /* Save pt_regs as 4th parameter */
|
|
|
|
GLOBAL(ftrace_regs_call)
|
|
call ftrace_stub
|
|
|
|
addl $4, %esp /* Skip pt_regs */
|
|
|
|
/* restore flags */
|
|
push 14*4(%esp)
|
|
popf
|
|
|
|
/* Move return ip back to its original location */
|
|
movl 12*4(%esp), %eax
|
|
movl %eax, 14*4(%esp)
|
|
|
|
popl %ebx
|
|
popl %ecx
|
|
popl %edx
|
|
popl %esi
|
|
popl %edi
|
|
popl %ebp
|
|
popl %eax
|
|
popl %ds
|
|
popl %es
|
|
popl %fs
|
|
popl %gs
|
|
|
|
/* use lea to not affect flags */
|
|
lea 3*4(%esp), %esp /* Skip orig_ax, ip and cs */
|
|
|
|
jmp .Lftrace_ret
|
|
#else /* ! CONFIG_DYNAMIC_FTRACE */
|
|
|
|
ENTRY(function_hook)
|
|
cmpl $__PAGE_OFFSET, %esp
|
|
jb ftrace_stub /* Paging not enabled yet? */
|
|
|
|
cmpl $ftrace_stub, ftrace_trace_function
|
|
jnz .Ltrace
|
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
cmpl $ftrace_stub, ftrace_graph_return
|
|
jnz ftrace_graph_caller
|
|
|
|
cmpl $ftrace_graph_entry_stub, ftrace_graph_entry
|
|
jnz ftrace_graph_caller
|
|
#endif
|
|
.globl ftrace_stub
|
|
ftrace_stub:
|
|
ret
|
|
|
|
/* taken from glibc */
|
|
.Ltrace:
|
|
pushl %eax
|
|
pushl %ecx
|
|
pushl %edx
|
|
movl 0xc(%esp), %eax
|
|
movl 0x4(%ebp), %edx
|
|
subl $MCOUNT_INSN_SIZE, %eax
|
|
|
|
call *ftrace_trace_function
|
|
|
|
popl %edx
|
|
popl %ecx
|
|
popl %eax
|
|
jmp ftrace_stub
|
|
END(function_hook)
|
|
#endif /* CONFIG_DYNAMIC_FTRACE */
|
|
|
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
ENTRY(ftrace_graph_caller)
|
|
pushl %eax
|
|
pushl %ecx
|
|
pushl %edx
|
|
movl 3*4(%esp), %eax
|
|
/* Even with frame pointers, fentry doesn't have one here */
|
|
#ifdef CC_USING_FENTRY
|
|
lea 4*4(%esp), %edx
|
|
movl $0, %ecx
|
|
#else
|
|
lea 0x4(%ebp), %edx
|
|
movl (%ebp), %ecx
|
|
#endif
|
|
subl $MCOUNT_INSN_SIZE, %eax
|
|
call prepare_ftrace_return
|
|
popl %edx
|
|
popl %ecx
|
|
popl %eax
|
|
ret
|
|
END(ftrace_graph_caller)
|
|
|
|
.globl return_to_handler
|
|
return_to_handler:
|
|
pushl %eax
|
|
pushl %edx
|
|
#ifdef CC_USING_FENTRY
|
|
movl $0, %eax
|
|
#else
|
|
movl %ebp, %eax
|
|
#endif
|
|
call ftrace_return_to_handler
|
|
movl %eax, %ecx
|
|
popl %edx
|
|
popl %eax
|
|
jmp *%ecx
|
|
#endif
|