2006-07-03 00:24:40 -07:00
/*
* Stack trace management functions
*
2009-01-31 02:03:42 +01:00
* Copyright ( C ) 2006 - 2009 Red Hat , Inc . , Ingo Molnar < mingo @ redhat . com >
2006-07-03 00:24:40 -07:00
*/
# include <linux/sched.h>
2017-02-08 18:51:35 +01:00
# include <linux/sched/debug.h>
2017-02-08 18:51:37 +01:00
# include <linux/sched/task_stack.h>
2006-07-03 00:24:40 -07:00
# include <linux/stacktrace.h>
2016-07-13 20:18:56 -04:00
# include <linux/export.h>
2008-11-22 13:28:47 +02:00
# include <linux/uaccess.h>
2006-09-26 10:52:34 +02:00
# include <asm/stacktrace.h>
2016-09-16 14:18:14 -05:00
# include <asm/unwind.h>
2006-07-03 00:24:40 -07:00
2016-09-16 14:18:14 -05:00
static int save_stack_address ( struct stack_trace * trace , unsigned long addr ,
bool nosched )
2006-07-03 00:24:40 -07:00
{
2010-06-03 21:32:43 +02:00
if ( nosched & & in_sched_functions ( addr ) )
2016-02-17 19:58:57 -08:00
return 0 ;
2016-09-16 14:18:14 -05:00
2006-09-26 10:52:34 +02:00
if ( trace - > skip > 0 ) {
trace - > skip - - ;
2016-02-17 19:58:57 -08:00
return 0 ;
2006-07-03 00:24:40 -07:00
}
2016-09-16 14:18:14 -05:00
if ( trace - > nr_entries > = trace - > max_entries )
return - 1 ;
trace - > entries [ trace - > nr_entries + + ] = addr ;
return 0 ;
2010-06-03 21:32:43 +02:00
}
2016-09-16 14:18:14 -05:00
static void __save_stack_trace ( struct stack_trace * trace ,
struct task_struct * task , struct pt_regs * regs ,
bool nosched )
2008-01-25 21:08:34 +01:00
{
2016-09-16 14:18:14 -05:00
struct unwind_state state ;
unsigned long addr ;
2008-01-25 21:08:34 +01:00
2016-09-16 14:18:14 -05:00
if ( regs )
save_stack_address ( trace , regs - > ip , nosched ) ;
2006-07-03 00:24:40 -07:00
2016-09-16 14:18:14 -05:00
for ( unwind_start ( & state , task , regs , NULL ) ; ! unwind_done ( & state ) ;
unwind_next_frame ( & state ) ) {
addr = unwind_get_return_address ( & state ) ;
if ( ! addr | | save_stack_address ( trace , addr , nosched ) )
break ;
}
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
}
2008-01-25 21:08:34 +01:00
2006-07-03 00:24:40 -07:00
/*
* Save stack - backtrace addresses into a stack_trace buffer .
*/
2007-05-08 00:23:29 -07:00
void save_stack_trace ( struct stack_trace * trace )
2006-07-03 00:24:40 -07:00
{
2016-09-16 14:18:14 -05:00
__save_stack_trace ( trace , current , NULL , false ) ;
2006-07-03 00:24:40 -07:00
}
2008-06-27 21:20:17 +02:00
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
2008-01-25 21:08:34 +01:00
2011-06-08 16:09:21 +09:00
void save_stack_trace_regs ( struct pt_regs * regs , struct stack_trace * trace )
2008-05-20 11:15:43 +02:00
{
2016-09-16 14:18:14 -05:00
__save_stack_trace ( trace , current , regs , false ) ;
2008-05-20 11:15:43 +02:00
}
2008-01-25 21:08:34 +01:00
void save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
{
2016-09-15 22:45:45 -07:00
if ( ! try_get_task_stack ( tsk ) )
return ;
2016-09-16 14:18:14 -05:00
__save_stack_trace ( trace , tsk , NULL , true ) ;
2016-09-15 22:45:45 -07:00
put_task_stack ( tsk ) ;
2008-01-25 21:08:34 +01:00
}
2008-06-27 21:20:17 +02:00
EXPORT_SYMBOL_GPL ( save_stack_trace_tsk ) ;
2008-11-22 13:28:47 +02:00
2017-02-13 19:42:28 -06:00
# ifdef CONFIG_HAVE_RELIABLE_STACKTRACE
# define STACKTRACE_DUMP_ONCE(task) ({ \
static bool __section ( . data . unlikely ) __dumped ; \
\
if ( ! __dumped ) { \
__dumped = true ; \
WARN_ON ( 1 ) ; \
show_stack ( task , NULL ) ; \
} \
} )
static int __save_stack_trace_reliable ( struct stack_trace * trace ,
struct task_struct * task )
{
struct unwind_state state ;
struct pt_regs * regs ;
unsigned long addr ;
for ( unwind_start ( & state , task , NULL , NULL ) ; ! unwind_done ( & state ) ;
unwind_next_frame ( & state ) ) {
regs = unwind_get_entry_regs ( & state ) ;
if ( regs ) {
/*
* Kernel mode registers on the stack indicate an
* in - kernel interrupt or exception ( e . g . , preemption
* or a page fault ) , which can make frame pointers
* unreliable .
*/
if ( ! user_mode ( regs ) )
return - EINVAL ;
/*
* The last frame contains the user mode syscall
* pt_regs . Skip it and finish the unwind .
*/
unwind_next_frame ( & state ) ;
if ( ! unwind_done ( & state ) ) {
STACKTRACE_DUMP_ONCE ( task ) ;
return - EINVAL ;
}
break ;
}
addr = unwind_get_return_address ( & state ) ;
/*
* A NULL or invalid return address probably means there ' s some
* generated code which __kernel_text_address ( ) doesn ' t know
* about .
*/
if ( ! addr ) {
STACKTRACE_DUMP_ONCE ( task ) ;
return - EINVAL ;
}
if ( save_stack_address ( trace , addr , false ) )
return - EINVAL ;
}
/* Check for stack corruption */
if ( unwind_error ( & state ) ) {
STACKTRACE_DUMP_ONCE ( task ) ;
return - EINVAL ;
}
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
return 0 ;
}
/*
* This function returns an error if it detects any unreliable features of the
* stack . Otherwise it guarantees that the stack trace is reliable .
*
* If the task is not ' current ' , the caller * must * ensure the task is inactive .
*/
int save_stack_trace_tsk_reliable ( struct task_struct * tsk ,
struct stack_trace * trace )
{
int ret ;
if ( ! try_get_task_stack ( tsk ) )
return - EINVAL ;
ret = __save_stack_trace_reliable ( trace , tsk ) ;
put_task_stack ( tsk ) ;
return ret ;
}
# endif /* CONFIG_HAVE_RELIABLE_STACKTRACE */
2008-11-22 13:28:47 +02:00
/* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */
2010-05-19 21:35:17 +02:00
struct stack_frame_user {
2008-11-22 13:28:47 +02:00
const void __user * next_fp ;
2008-11-23 12:39:06 +02:00
unsigned long ret_addr ;
2008-11-22 13:28:47 +02:00
} ;
2010-05-19 21:35:17 +02:00
static int
copy_stack_frame ( const void __user * fp , struct stack_frame_user * frame )
2008-11-22 13:28:47 +02:00
{
int ret ;
if ( ! access_ok ( VERIFY_READ , fp , sizeof ( * frame ) ) )
return 0 ;
ret = 1 ;
pagefault_disable ( ) ;
if ( __copy_from_user_inatomic ( frame , fp , sizeof ( * frame ) ) )
ret = 0 ;
pagefault_enable ( ) ;
return ret ;
}
2008-11-23 12:39:06 +02:00
static inline void __save_stack_trace_user ( struct stack_trace * trace )
{
const struct pt_regs * regs = task_pt_regs ( current ) ;
const void __user * fp = ( const void __user * ) regs - > bp ;
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = regs - > ip ;
while ( trace - > nr_entries < trace - > max_entries ) {
2010-05-19 21:35:17 +02:00
struct stack_frame_user frame ;
2008-11-23 12:39:06 +02:00
frame . next_fp = NULL ;
frame . ret_addr = 0 ;
if ( ! copy_stack_frame ( fp , & frame ) )
break ;
if ( ( unsigned long ) fp < regs - > sp )
break ;
if ( frame . ret_addr ) {
trace - > entries [ trace - > nr_entries + + ] =
frame . ret_addr ;
}
if ( fp = = frame . next_fp )
break ;
fp = frame . next_fp ;
}
}
2008-11-22 13:28:47 +02:00
void save_stack_trace_user ( struct stack_trace * trace )
{
/*
* Trace user stack if we are not a kernel thread
*/
if ( current - > mm ) {
2008-11-23 12:39:06 +02:00
__save_stack_trace_user ( trace ) ;
2008-11-22 13:28:47 +02:00
}
if ( trace - > nr_entries < trace - > max_entries )
trace - > entries [ trace - > nr_entries + + ] = ULONG_MAX ;
}