summaryrefslogblamecommitdiffstats
path: root/cpukit/libmisc/stackchk/check.c
blob: abbd0e0845c8ed3fd001a521a759e1a5c939d161 (plain) (tree)
1
2
3
4
5
6
7
8
9
10




                                                    
  
                                                        


                                                         


   
                            
                                                    
  

                                                           
                                         
  

   



                    
                  
                     
 


                   
                        
                          
                           
                               
                                   






                                                                    





                                                          

      






                                              
  
                                                                     
   
                                    



                                                          





                                                                

  



                                                                     
                                                      
                                  

 
                       
                                          
                                                                      
 
                                 
                   

                                                     
                   
     

                                                               
        
              


  

                                                                 
   
                                

                                                 
                                                  
 

                                                                  
 
                                                      


                        





                                                                               
                                                 
                                                                              
 

                                                                 
 
                                                      
                                                       


      



                                                                    
                                                   
                                             
 




                                                                           

  

                                                                   
   

                                                      
 








                                                                          
  
                                        
   
                                          
                                       


                            
                                 
 
                                                           




                                                              

              

 





































                                                                               
  
                                 
  


                                                         


                                                                 

                                          
                 
 
 


                                                                     
 
                             
                                                                       

                                                                   

                                   
    

                                                
         
                                                                          
                                

                                                   


                    
                                                                                 
                                         

                                                    

      
 

                                                               
             
                                   
                                                                               
        
     

        



                                     

 
  
                                        
   
                                          

                                       

 

                  
 
    
                                                              
     
                                                        
 
                                                       


                                                         


   


                  
                                         
 
                                                                        

    
                                                                   
     
               




                                   
                                                     
                
               

 

                                 



               




                                                               
 

                                         

                               




                                                              
 

                                                     

                               
        



                   





                                         

 



                            










                                                                    

               
                                                                                             




                                                          
        
    
 
                                
                                                    

                                     
   
 
               

 





















                                                           
                          





                               

                      



           
  
                                    

   
                                                  
                              
 
 


                     


                                                           
                                                                                        
    
 
                                                   



                                             
 








                                                           



                                             


                                                           
 
/**
 * @file
 *
 * @brief Stack Overflow Check User Extension Set
 * @ingroup libmisc_stackchk Stack Checker Mechanism
 *
 * NOTE:  This extension set automatically determines at
 *         initialization time whether the stack for this
 *         CPU grows up or down and installs the correct
 *         extension routines for that direction.
 */

/*
 *  COPYRIGHT (c) 1989-2010.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems.h>
#include <inttypes.h>

#include <string.h>
#include <stdlib.h>

#include <rtems/bspIo.h>
#include <rtems/printer.h>
#include <rtems/stackchk.h>
#include <rtems/score/percpu.h>
#include <rtems/score/threadimpl.h>

/*
 *  This structure is used to fill in and compare the "end of stack"
 *  marker pattern.
 *  pattern area must be a multiple of 4 words.
 */

#if !defined(CPU_STACK_CHECK_PATTERN_INITIALIZER)
#define CPU_STACK_CHECK_PATTERN_INITIALIZER \
  { \
    0xFEEDF00D, 0x0BAD0D06, /* FEED FOOD to  BAD DOG */ \
    0xDEADF00D, 0x600D0D06  /* DEAD FOOD but GOOD DOG */ \
  }
#endif

/*
 *  The pattern used to fill the entire stack.
 */

#define BYTE_PATTERN 0xA5
#define U32_PATTERN 0xA5A5A5A5

/*
 *  Variable to indicate when the stack checker has been initialized.
 */
static bool Stack_check_Initialized;

/*
 *  The "magic pattern" used to mark the end of the stack.
 */
static const uint32_t Stack_check_Pattern[] =
  CPU_STACK_CHECK_PATTERN_INITIALIZER;

#define PATTERN_SIZE_BYTES sizeof(Stack_check_Pattern)

#define PATTERN_SIZE_WORDS RTEMS_ARRAY_SIZE(Stack_check_Pattern)

/*
 * Helper function to report if the actual stack pointer is in range.
 *
 * NOTE: This uses a GCC specific method.
 */
static inline bool Stack_check_Frame_pointer_in_range(
  const Thread_Control *the_thread
)
{
  #if defined(__GNUC__)
    void *sp = __builtin_frame_address(0);
    const Stack_Control *the_stack = &the_thread->Start.Initial_stack;

    if ( sp < the_stack->area ) {
      return false;
    }
    if ( sp > (the_stack->area + the_stack->size) ) {
      return false;
    }
  #else
    #error "How do I check stack bounds on a non-GNU compiler?"
  #endif
  return true;
}

/*
 *  Where the pattern goes in the stack area is dependent upon
 *  whether the stack grow to the high or low area of the memory.
 */
#if (CPU_STACK_GROWS_UP == TRUE)
  #define Stack_check_Get_pattern( _the_stack ) \
    ((char *)(_the_stack)->area + \
         (_the_stack)->size - PATTERN_SIZE_BYTES )

  #define Stack_check_Calculate_used( _low, _size, _high_water ) \
      ((char *)(_high_water) - (char *)(_low))

  #define Stack_check_usable_stack_start(_the_stack) \
    ((_the_stack)->area)

#else
  /*
   * We need this magic offset because during a task delete the task stack will
   * be freed before we enter the task switch extension which checks the stack.
   * The task stack free operation will write the next and previous pointers
   * for the free list into this area.
   */
  #define Stack_check_Get_pattern( _the_stack ) \
    ((char *)(_the_stack)->area + sizeof(Heap_Block) - HEAP_BLOCK_HEADER_SIZE)

  #define Stack_check_Calculate_used( _low, _size, _high_water) \
      ( ((char *)(_low) + (_size)) - (char *)(_high_water) )

  #define Stack_check_usable_stack_start(_the_stack) \
      ((char *)(_the_stack)->area + PATTERN_SIZE_BYTES)

#endif

/*
 *  The assumption is that if the pattern gets overwritten, the task
 *  is too close.  This defines the usable stack memory.
 */
#define Stack_check_usable_stack_size(_the_stack) \
    ((_the_stack)->size - PATTERN_SIZE_BYTES)

#if defined(RTEMS_SMP)
static Stack_Control Stack_check_Interrupt_stack[ CPU_MAXIMUM_PROCESSORS ];
#else
static Stack_Control Stack_check_Interrupt_stack[ 1 ];
#endif

/*
 *  Fill an entire stack area with BYTE_PATTERN.  This will be used
 *  to check for amount of actual stack used.
 */
#define Stack_check_Dope_stack(_stack) \
  memset((_stack)->area, BYTE_PATTERN, (_stack)->size)

static bool Stack_check_Is_pattern_valid(const Thread_Control *the_thread)
{
  return memcmp(
    Stack_check_Get_pattern(&the_thread->Start.Initial_stack),
    Stack_check_Pattern,
    PATTERN_SIZE_BYTES
  ) == 0;
}

/*
 *  rtems_stack_checker_create_extension
 */
bool rtems_stack_checker_create_extension(
  Thread_Control *running RTEMS_UNUSED,
  Thread_Control *the_thread
)
{
  Stack_check_Initialized = true;

  Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
  memcpy(
    Stack_check_Get_pattern(&the_thread->Start.Initial_stack),
    Stack_check_Pattern,
    PATTERN_SIZE_BYTES
  );

  return true;
}

void rtems_stack_checker_begin_extension( Thread_Control *executing )
{
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
  Per_CPU_Control *cpu_self;
  uint32_t         cpu_self_index;
  Stack_Control   *stack;

  /*
   * If appropriate, set up the interrupt stack of the current processor for
   * high water testing also.  This must be done after multi-threading started,
   * since the initialization stacks may reuse the interrupt stacks.  Disable
   * thread dispatching in SMP configurations to prevent thread migration.
   * Writing to the interrupt stack is only safe if done from the corresponding
   * processor in thread context.
   */

#if defined(RTEMS_SMP)
  cpu_self = _Thread_Dispatch_disable();
#else
  cpu_self = _Per_CPU_Get();
#endif

  cpu_self_index = _Per_CPU_Get_index( cpu_self );
  stack = &Stack_check_Interrupt_stack[ cpu_self_index ];

  if ( stack->area == NULL ) {
    stack->area = cpu_self->interrupt_stack_low;
    stack->size = (size_t) ( (char *) cpu_self->interrupt_stack_high -
      (char *) cpu_self->interrupt_stack_low );
    Stack_check_Dope_stack( stack );
  }

#if defined(RTEMS_SMP)
  _Thread_Dispatch_enable( cpu_self );
#endif
#endif
}

/*
 *  Stack_check_report_blown_task
 *
 *  Report a blown stack.  Needs to be a separate routine
 *  so that interrupt handlers can use this too.
 *
 *  NOTE: The system is in a questionable state... we may not get
 *        the following message out.
 */
static void Stack_check_report_blown_task(
  const Thread_Control *running,
  bool pattern_ok
)
{
  const Stack_Control *stack = &running->Start.Initial_stack;
  void                *pattern_area = Stack_check_Get_pattern(stack);
  char                 name[32];

  printk("BLOWN STACK!!!\n");
  printk("task control block: 0x%08" PRIxPTR "\n", (intptr_t) running);
  printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
  printk(
    "task name: 0x%08" PRIx32 "\n",
    running->Object.name.name_u32
  );
  _Thread_Get_name(running, name, sizeof(name));
  printk("task name string: %s\n", name);
  printk(
    "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
    (unsigned long) stack->size,
    (intptr_t) stack->area,
    (intptr_t) ((char *) stack->area + stack->size)
  );
  if (!pattern_ok) {
    printk(
      "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
      (unsigned long) PATTERN_SIZE_BYTES,
      (intptr_t) pattern_area,
      (intptr_t) (pattern_area + PATTERN_SIZE_BYTES)
    );
  }

  #if defined(RTEMS_MULTIPROCESSING)
    if (rtems_configuration_get_user_multiprocessing_table()) {
      printk(
        "node: 0x%08" PRIxPTR "\n",
          (intptr_t) rtems_configuration_get_user_multiprocessing_table()->node
      );
    }
  #endif

  rtems_fatal(
    RTEMS_FATAL_SOURCE_STACK_CHECKER,
    running->Object.name.name_u32
  );
}

/*
 *  rtems_stack_checker_switch_extension
 */
void rtems_stack_checker_switch_extension(
  Thread_Control *running RTEMS_UNUSED,
  Thread_Control *heir RTEMS_UNUSED
)
{
  bool sp_ok;
  bool pattern_ok;

  /*
   *  Check for an out of bounds stack pointer or an overwrite
   */
  sp_ok = Stack_check_Frame_pointer_in_range( running );

  pattern_ok = Stack_check_Is_pattern_valid( running );

  if ( !sp_ok || !pattern_ok ) {
    Stack_check_report_blown_task( running, pattern_ok );
  }
}

/*
 *  Check if blown
 */
bool rtems_stack_checker_is_blown( void )
{
  rtems_stack_checker_switch_extension( _Thread_Get_executing(), NULL );

  /*
   * The Stack Pointer and the Pattern Area are OK so return false.
   */
  return false;
}

/*
 * Stack_check_find_high_water_mark
 */
static inline void *Stack_check_find_high_water_mark(
  const void *s,
  size_t      n
)
{
  const uint32_t   *base, *ebase;
  uint32_t   length;

  base = s;
  length = n/4;

  #if ( CPU_STACK_GROWS_UP == TRUE )
    /*
     * start at higher memory and find first word that does not
     * match pattern
     */

    base += length - 1;
    for (ebase = s; base > ebase; base--)
      if (*base != U32_PATTERN)
        return (void *) base;
  #else
    /*
     * start at lower memory and find first word that does not
     * match pattern
     */

    base += PATTERN_SIZE_WORDS;
    for (ebase = base + length; base < ebase; base++)
      if (*base != U32_PATTERN)
        return (void *) base;
  #endif

  return (void *)0;
}

static bool Stack_check_Dump_stack_usage(
  const Stack_Control *stack,
  const void          *current,
  const char          *name,
  uint32_t             id,
  const rtems_printer *printer
)
{
  uint32_t  size;
  uint32_t  used;
  void     *low;
  void     *high_water_mark;

  low  = Stack_check_usable_stack_start(stack);
  size = Stack_check_usable_stack_size(stack);

  high_water_mark = Stack_check_find_high_water_mark(low, size);

  if ( high_water_mark )
    used = Stack_check_Calculate_used( low, size, high_water_mark );
  else
    used = 0;

  rtems_printf(
    printer,
    "0x%08" PRIx32 " %-21s 0x%08" PRIxPTR " 0x%08" PRIxPTR " 0x%08" PRIxPTR " %6" PRId32 " ",
    id,
    name,
    (uintptr_t) stack->area,
    (uintptr_t) stack->area + (uintptr_t) stack->size - 1,
    (uintptr_t) current,
    size
  );

  if (Stack_check_Initialized) {
    rtems_printf( printer, "%6" PRId32 "\n", used );
  } else {
    rtems_printf( printer, "N/A\n" );
  }

  return false;
}

static bool Stack_check_Dump_threads_usage(
  Thread_Control *the_thread,
  void           *arg
)
{
  char                 name[ 22 ];
  const rtems_printer *printer;

  printer = arg;
  _Thread_Get_name( the_thread, name, sizeof( name ) );
  Stack_check_Dump_stack_usage(
    &the_thread->Start.Initial_stack,
    (void *) _CPU_Context_Get_SP( &the_thread->Registers ),
    name,
    the_thread->Object.id,
    printer
  );
  return false;
}

static void Stack_check_Dump_interrupt_stack_usage(
  const Stack_Control *stack,
  uint32_t             id,
  const rtems_printer *printer
)
{
  Stack_check_Dump_stack_usage(
    stack,
    NULL,
    "Interrupt Stack",
    id,
    printer
  );
}

/*
 *  rtems_stack_checker_report_usage
 */

void rtems_stack_checker_report_usage_with_plugin(
  const rtems_printer* printer
)
{
  uint32_t cpu_max;
  uint32_t cpu_index;

  rtems_printf(
     printer,
     "                             STACK USAGE BY THREAD\n"
     "ID         NAME                  LOW        HIGH       CURRENT     AVAIL   USED\n"
  );

  /* iterate over all threads and dump the usage */
  rtems_task_iterate(
    Stack_check_Dump_threads_usage,
    RTEMS_DECONST( rtems_printer *, printer )
  );

  cpu_max = rtems_get_processor_count();

  for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
    Stack_check_Dump_interrupt_stack_usage(
      &Stack_check_Interrupt_stack[ cpu_index ],
      cpu_index,
      printer
    );
  }
}

void rtems_stack_checker_report_usage( void )
{
  rtems_printer printer;
  rtems_print_printer_printk(&printer);
  rtems_stack_checker_report_usage_with_plugin( &printer );
}