2019-05-19 13:08:55 +01:00
// SPDX-License-Identifier: GPL-2.0-only
2011-07-22 10:58:34 -04:00
# include <linux/export.h>
2021-10-21 09:55:35 +09:00
# include <linux/kprobes.h>
2007-04-28 09:59:37 +01:00
# include <linux/sched.h>
2017-02-08 18:51:35 +01:00
# include <linux/sched/debug.h>
2007-04-28 09:59:37 +01:00
# include <linux/stacktrace.h>
2017-11-24 23:54:22 +00:00
# include <asm/sections.h>
2009-02-11 13:07:53 +01:00
# include <asm/stacktrace.h>
2014-05-03 16:17:16 +01:00
# include <asm/traps.h>
2009-02-11 13:07:53 +01:00
2022-08-26 09:06:22 +01:00
# include "reboot.h"
2009-02-11 13:07:53 +01:00
# if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
/*
* Unwind the current stack frame and store the new register values in the
* structure passed as argument . Unwinding is equivalent to a function return ,
* hence the new PC value rather than LR should be used for backtrace .
*
* With framepointer enabled , a simple function prologue looks like this :
* mov ip , sp
* stmdb sp ! , { fp , ip , lr , pc }
* sub fp , ip , # 4
*
* A simple function epilogue looks like this :
* ldm sp , { fp , sp , pc }
*
2020-07-10 20:23:37 +01:00
* When compiled with clang , pc and sp are not pushed . A simple function
* prologue looks like this when built with clang :
*
* stmdb { . . . , fp , lr }
* add fp , sp , # x
* sub sp , sp , # y
*
* A simple function epilogue looks like this when built with clang :
*
* sub sp , fp , # x
* ldm { . . . , fp , pc }
*
*
2009-02-11 13:07:53 +01:00
* Note that with framepointer enabled , even the leaf functions have the same
* prologue and epilogue , therefore we can ignore the LR value in this case .
*/
2022-08-26 09:06:22 +01:00
extern unsigned long call_with_stack_end ;
static int frame_pointer_check ( struct stackframe * frame )
2007-04-28 09:59:37 +01:00
{
2009-02-11 13:07:53 +01:00
unsigned long high , low ;
unsigned long fp = frame - > fp ;
2022-08-26 09:06:22 +01:00
unsigned long pc = frame - > pc ;
/*
* call_with_stack ( ) is the only place we allow SP to jump from one
* stack to another , with FP and SP pointing to different stacks ,
* skipping the FP boundary check at this point .
*/
if ( pc > = ( unsigned long ) & call_with_stack & &
pc < ( unsigned long ) & call_with_stack_end )
return 0 ;
2007-04-28 09:59:37 +01:00
2009-02-11 13:07:53 +01:00
/* only go to a higher address on the stack */
low = frame - > sp ;
2010-11-04 18:22:51 +01:00
high = ALIGN ( low , THREAD_SIZE ) ;
2007-04-28 09:59:37 +01:00
2020-07-10 20:23:37 +01:00
/* check current frame pointer is within bounds */
2022-08-26 09:06:22 +01:00
# ifdef CONFIG_CC_IS_CLANG
2020-07-10 20:23:37 +01:00
if ( fp < low + 4 | | fp > high - 4 )
return - EINVAL ;
# else
2013-12-05 14:23:48 +01:00
if ( fp < low + 12 | | fp > high - 4 )
2009-02-11 13:07:53 +01:00
return - EINVAL ;
2022-08-26 09:06:22 +01:00
# endif
return 0 ;
}
int notrace unwind_frame ( struct stackframe * frame )
{
unsigned long fp = frame - > fp ;
if ( frame_pointer_check ( frame ) )
return - EINVAL ;
2007-04-28 09:59:37 +01:00
2022-08-26 09:08:46 +01:00
/*
* When we unwind through an exception stack , include the saved PC
* value into the stack trace .
*/
if ( frame - > ex_frame ) {
struct pt_regs * regs = ( struct pt_regs * ) frame - > sp ;
/*
* We check that ' regs + sizeof ( struct pt_regs ) ' ( that is ,
* & regs [ 1 ] ) does not exceed the bottom of the stack to avoid
* accessing data outside the task ' s stack . This may happen
* when frame - > ex_frame is a false positive .
*/
if ( ( unsigned long ) & regs [ 1 ] > ALIGN ( frame - > sp , THREAD_SIZE ) )
return - EINVAL ;
frame - > pc = regs - > ARM_pc ;
frame - > ex_frame = false ;
return 0 ;
}
2009-02-11 13:07:53 +01:00
/* restore the registers from the stack frame */
2022-08-26 09:06:22 +01:00
# ifdef CONFIG_CC_IS_CLANG
frame - > sp = frame - > fp ;
frame - > fp = READ_ONCE_NOCHECK ( * ( unsigned long * ) ( fp ) ) ;
frame - > pc = READ_ONCE_NOCHECK ( * ( unsigned long * ) ( fp + 4 ) ) ;
# else
2022-04-01 10:52:47 +01:00
frame - > fp = READ_ONCE_NOCHECK ( * ( unsigned long * ) ( fp - 12 ) ) ;
frame - > sp = READ_ONCE_NOCHECK ( * ( unsigned long * ) ( fp - 8 ) ) ;
frame - > pc = READ_ONCE_NOCHECK ( * ( unsigned long * ) ( fp - 4 ) ) ;
2020-07-10 20:23:37 +01:00
# endif
2021-10-21 09:55:35 +09:00
# ifdef CONFIG_KRETPROBES
if ( is_kretprobe_trampoline ( frame - > pc ) )
frame - > pc = kretprobe_find_ret_addr ( frame - > tsk ,
( void * ) frame - > fp , & frame - > kr_cur ) ;
# endif
2007-04-28 09:59:37 +01:00
2022-08-26 09:08:46 +01:00
if ( in_entry_text ( frame - > pc ) )
frame - > ex_frame = true ;
2007-04-28 09:59:37 +01:00
return 0 ;
}
2009-02-11 13:07:53 +01:00
# endif
2009-07-21 09:56:27 +01:00
void notrace walk_stackframe ( struct stackframe * frame ,
2009-02-11 13:07:53 +01:00
int ( * fn ) ( struct stackframe * , void * ) , void * data )
{
while ( 1 ) {
int ret ;
if ( fn ( frame , data ) )
break ;
ret = unwind_frame ( frame ) ;
if ( ret < 0 )
break ;
}
}
2007-05-15 20:37:20 +01:00
EXPORT_SYMBOL ( walk_stackframe ) ;
2007-04-28 09:59:37 +01:00
# ifdef CONFIG_STACKTRACE
struct stack_trace_data {
struct stack_trace * trace ;
2008-04-24 01:31:46 -04:00
unsigned int no_sched_functions ;
2007-04-28 09:59:37 +01:00
unsigned int skip ;
} ;
static int save_trace ( struct stackframe * frame , void * d )
{
struct stack_trace_data * data = d ;
struct stack_trace * trace = data - > trace ;
2009-02-11 13:07:53 +01:00
unsigned long addr = frame - > pc ;
2007-04-28 09:59:37 +01:00
2008-04-24 01:31:46 -04:00
if ( data - > no_sched_functions & & in_sched_functions ( addr ) )
return 0 ;
2007-04-28 09:59:37 +01:00
if ( data - > skip ) {
data - > skip - - ;
return 0 ;
}
2008-04-24 01:31:46 -04:00
trace - > entries [ trace - > nr_entries + + ] = addr ;
2007-04-28 09:59:37 +01:00
return trace - > nr_entries > = trace - > max_entries ;
}
2014-05-03 11:03:28 +01:00
/* This must be noinline to so that our skip calculation works correctly */
static noinline void __save_stack_trace ( struct task_struct * tsk ,
struct stack_trace * trace , unsigned int nosched )
2007-04-28 09:59:37 +01:00
{
struct stack_trace_data data ;
2009-02-11 13:07:53 +01:00
struct stackframe frame ;
2007-04-28 09:59:37 +01:00
data . trace = trace ;
data . skip = trace - > skip ;
2014-05-03 11:03:28 +01:00
data . no_sched_functions = nosched ;
2008-04-24 01:31:46 -04:00
if ( tsk ! = current ) {
# ifdef CONFIG_SMP
/*
2011-01-15 09:27:04 +00:00
* What guarantees do we have here that ' tsk ' is not
* running on another CPU ? For now , ignore it as we
* can ' t guarantee we won ' t explode .
2008-04-24 01:31:46 -04:00
*/
2011-01-15 09:27:04 +00:00
return ;
2008-04-24 01:31:46 -04:00
# else
2009-02-11 13:07:53 +01:00
frame . fp = thread_saved_fp ( tsk ) ;
frame . sp = thread_saved_sp ( tsk ) ;
frame . lr = 0 ; /* recovered from the stack */
frame . pc = thread_saved_pc ( tsk ) ;
2008-04-24 01:31:46 -04:00
# endif
} else {
2014-05-03 11:03:28 +01:00
/* We don't want this function nor the caller */
data . skip + = 2 ;
2009-02-11 13:07:53 +01:00
frame . fp = ( unsigned long ) __builtin_frame_address ( 0 ) ;
2014-09-27 00:31:08 +01:00
frame . sp = current_stack_pointer ;
2009-02-11 13:07:53 +01:00
frame . lr = ( unsigned long ) __builtin_return_address ( 0 ) ;
2022-03-09 12:06:02 +00:00
here :
frame . pc = ( unsigned long ) & & here ;
2008-04-24 01:31:46 -04:00
}
2021-10-21 09:55:35 +09:00
# ifdef CONFIG_KRETPROBES
frame . kr_cur = NULL ;
frame . tsk = tsk ;
# endif
2022-08-26 09:08:46 +01:00
# ifdef CONFIG_UNWINDER_FRAME_POINTER
frame . ex_frame = false ;
# endif
2007-04-28 09:59:37 +01:00
2009-02-11 13:07:53 +01:00
walk_stackframe ( & frame , save_trace , & data ) ;
2008-04-24 01:31:46 -04:00
}
2014-05-04 16:27:41 +01:00
void save_stack_trace_regs ( struct pt_regs * regs , struct stack_trace * trace )
{
struct stack_trace_data data ;
struct stackframe frame ;
data . trace = trace ;
data . skip = trace - > skip ;
data . no_sched_functions = 0 ;
frame . fp = regs - > ARM_fp ;
frame . sp = regs - > ARM_sp ;
frame . lr = regs - > ARM_lr ;
frame . pc = regs - > ARM_pc ;
2021-10-21 09:55:35 +09:00
# ifdef CONFIG_KRETPROBES
frame . kr_cur = NULL ;
frame . tsk = current ;
# endif
2022-08-26 09:08:46 +01:00
# ifdef CONFIG_UNWINDER_FRAME_POINTER
frame . ex_frame = in_entry_text ( frame . pc ) ;
# endif
2014-05-04 16:27:41 +01:00
walk_stackframe ( & frame , save_trace , & data ) ;
}
2014-05-03 11:03:28 +01:00
void save_stack_trace_tsk ( struct task_struct * tsk , struct stack_trace * trace )
{
__save_stack_trace ( tsk , trace , 1 ) ;
}
2017-08-07 19:44:01 +01:00
EXPORT_SYMBOL ( save_stack_trace_tsk ) ;
2014-05-03 11:03:28 +01:00
2008-04-24 01:31:46 -04:00
void save_stack_trace ( struct stack_trace * trace )
{
2014-05-03 11:03:28 +01:00
__save_stack_trace ( current , trace , 0 ) ;
2007-04-28 09:59:37 +01:00
}
2008-07-03 09:17:55 +02:00
EXPORT_SYMBOL_GPL ( save_stack_trace ) ;
2007-04-28 09:59:37 +01:00
# endif