Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:19

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file 
0005  * 
0006  * @ingroup libmisc_stackchk Stack Checker Mechanism 
0007  * 
0008  * @brief This file contains the Stack Overflow Check user extension set.
0009  * 
0010  * @note This extension set uses conditional compilation to account for 
0011  *       stack growth direction.
0012  */         
0013 
0014 /*
0015  *  COPYRIGHT (c) 1989-2024 On-Line Applications Research Corporation (OAR).
0016  *  COPYRIGHT (c) 2024 Mohamed Hassan <muhammad.hamdy.hassan@gmail.com>
0017  *
0018  * Redistribution and use in source and binary forms, with or without
0019  * modification, are permitted provided that the following conditions
0020  * are met:
0021  * 1. Redistributions of source code must retain the above copyright
0022  *    notice, this list of conditions and the following disclaimer.
0023  * 2. Redistributions in binary form must reproduce the above copyright
0024  *    notice, this list of conditions and the following disclaimer in the
0025  *    documentation and/or other materials provided with the distribution.
0026  *
0027  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0028  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0029  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0030  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0031  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0032  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0033  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0034  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0035  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0036  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0037  * POSSIBILITY OF SUCH DAMAGE.
0038  *
0039  */
0040 
0041 #ifdef HAVE_CONFIG_H
0042 #include "config.h"
0043 #endif
0044 
0045 #include <rtems.h>
0046 #include <inttypes.h>
0047 
0048 #include <string.h>
0049 #include <stdlib.h>
0050 
0051 #include <rtems/bspIo.h>
0052 #include <rtems/printer.h>
0053 #include <rtems/stackchk.h>
0054 #include <rtems/sysinit.h>
0055 #include <rtems/score/address.h>
0056 #include <rtems/score/percpu.h>
0057 #include <rtems/score/smp.h>
0058 #include <rtems/score/threadimpl.h>
0059 
0060 /*
0061  *  This structure is used to fill in and compare the "end of stack"
0062  *  marker pattern.
0063  *  pattern area must be a multiple of 4 words.
0064  */
0065 
0066 #if !defined(CPU_STACK_CHECK_PATTERN_INITIALIZER)
0067 #define CPU_STACK_CHECK_PATTERN_INITIALIZER \
0068   { \
0069     0xFEEDF00D, 0x0BAD0D06, /* FEED FOOD to  BAD DOG */ \
0070     0xDEADF00D, 0x600D0D06  /* DEAD FOOD but GOOD DOG */ \
0071   }
0072 #endif
0073 
0074 /*
0075  *  The pattern used to fill the entire stack.
0076  */
0077 
0078 #define BYTE_PATTERN 0xA5
0079 #define U32_PATTERN 0xA5A5A5A5
0080 
0081 /*
0082  *  Variable to indicate when the stack checker has been initialized.
0083  */
0084 static bool Stack_check_Initialized;
0085 
0086 /*
0087  *  The "magic pattern" used to mark the end of the stack.
0088  */
0089 static const uint32_t Stack_check_Sanity_pattern[] =
0090   CPU_STACK_CHECK_PATTERN_INITIALIZER;
0091 
0092 #define SANITY_PATTERN_SIZE_BYTES sizeof(Stack_check_Sanity_pattern)
0093 
0094 #define SANITY_PATTERN_SIZE_WORDS RTEMS_ARRAY_SIZE(Stack_check_Sanity_pattern)
0095 
0096 /*
0097  * Helper function to report if the actual stack pointer is in range.
0098  *
0099  * NOTE: This uses a GCC specific method.
0100  */
0101 static inline bool Stack_check_Frame_pointer_in_range(
0102   const Thread_Control *the_thread
0103 )
0104 {
0105   #if defined(__GNUC__)
0106     void *sp = __builtin_frame_address(0);
0107     const Stack_Control *the_stack = &the_thread->Start.Initial_stack;
0108 
0109     if ( sp < the_stack->area ) {
0110       return false;
0111     }
0112     if ( sp > (the_stack->area + the_stack->size) ) {
0113       return false;
0114     }
0115   #else
0116     #error "How do I check stack bounds on a non-GNU compiler?"
0117   #endif
0118   return true;
0119 }
0120 
0121 /*
0122  *  Where the pattern goes in the stack area is dependent upon
0123  *  whether the stack grow to the high or low area of the memory.
0124  */
0125 #if (CPU_STACK_GROWS_UP == TRUE)
0126   #define Stack_check_Get_pattern( _the_stack ) \
0127     ((char *)(_the_stack)->area + \
0128          (_the_stack)->size - SANITY_PATTERN_SIZE_BYTES )
0129 
0130   #define Stack_check_Calculate_used( _low, _size, _high_water ) \
0131       ((char *)(_high_water) - (char *)(_low))
0132 
0133   #define Stack_check_Usable_stack_start(_the_stack) \
0134     ((_the_stack)->area)
0135 
0136 #else
0137   #define Stack_check_Get_pattern( _the_stack ) \
0138     ((char *)(_the_stack)->area)
0139 
0140   #define Stack_check_Calculate_used( _low, _size, _high_water) \
0141       ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
0142 
0143   #define Stack_check_Usable_stack_start(_the_stack) \
0144       ((char *)(_the_stack)->area + SANITY_PATTERN_SIZE_BYTES)
0145 
0146 #endif
0147 
0148 /*
0149  *  The assumption is that if the pattern gets overwritten, the task
0150  *  is too close. This defines the usable stack memory.
0151  */
0152 #define Stack_check_Usable_stack_size(_the_stack) \
0153     ((_the_stack)->size - SANITY_PATTERN_SIZE_BYTES)
0154 
0155 #if defined(RTEMS_SMP)
0156 static Stack_Control Stack_check_Interrupt_stack[ CPU_MAXIMUM_PROCESSORS ];
0157 #else
0158 static Stack_Control Stack_check_Interrupt_stack[ 1 ];
0159 #endif
0160 
0161 /*
0162  *  Fill an entire stack area with BYTE_PATTERN. This will be used
0163  *  to check for amount of actual stack used.
0164  */
0165 static void Stack_check_Dope_stack( Stack_Control *stack )
0166 {
0167   memset(
0168     Stack_check_Usable_stack_start( stack ),
0169     BYTE_PATTERN,
0170     Stack_check_Usable_stack_size( stack )
0171   );
0172 }
0173 
0174 static void Stack_check_Add_sanity_pattern( Stack_Control *stack )
0175 {
0176   memcpy(
0177     Stack_check_Get_pattern( stack ),
0178     Stack_check_Sanity_pattern,
0179     SANITY_PATTERN_SIZE_BYTES
0180   );
0181 }
0182 
0183 static bool Stack_check_Is_sanity_pattern_valid( const Stack_Control *stack )
0184 {
0185   return memcmp(
0186     Stack_check_Get_pattern( stack ),
0187     Stack_check_Sanity_pattern,
0188     SANITY_PATTERN_SIZE_BYTES
0189   ) == 0;
0190 }
0191 
0192 /*
0193  *  rtems_stack_checker_create_extension
0194  */
0195 bool rtems_stack_checker_create_extension(
0196   Thread_Control *running RTEMS_UNUSED,
0197   Thread_Control *the_thread
0198 )
0199 {
0200   Stack_check_Initialized = true;
0201 
0202   Stack_check_Dope_stack( &the_thread->Start.Initial_stack );
0203   Stack_check_Add_sanity_pattern( &the_thread->Start.Initial_stack );
0204 
0205   return true;
0206 }
0207 
0208 void rtems_stack_checker_begin_extension( Thread_Control *executing )
0209 {
0210   Per_CPU_Control *cpu_self;
0211   uint32_t         cpu_self_index;
0212   Stack_Control   *stack;
0213 
0214   /*
0215    * If appropriate, set up the interrupt stack of the current processor for
0216    * high water testing also. This must be done after multi-threading started,
0217    * since the initialization stacks may reuse the interrupt stacks. Disable
0218    * thread dispatching in SMP configurations to prevent thread migration.
0219    * Writing to the interrupt stack is only safe if done from the corresponding
0220    * processor in thread context.
0221    */
0222 
0223 #if defined(RTEMS_SMP)
0224   cpu_self = _Thread_Dispatch_disable();
0225 #else
0226   cpu_self = _Per_CPU_Get();
0227 #endif
0228 
0229   cpu_self_index = _Per_CPU_Get_index( cpu_self );
0230   stack = &Stack_check_Interrupt_stack[ cpu_self_index ];
0231 
0232   if ( stack->area == NULL ) {
0233     stack->area = cpu_self->interrupt_stack_low;
0234     stack->size = (size_t) ( (char *) cpu_self->interrupt_stack_high -
0235       (char *) cpu_self->interrupt_stack_low );
0236 
0237     /*
0238      * Sanity pattern has been added by Stack_check_Prepare_interrupt_stack()
0239      */
0240     if ( !Stack_check_Is_sanity_pattern_valid( stack ) ) {
0241       rtems_fatal(
0242         RTEMS_FATAL_SOURCE_STACK_CHECKER,
0243         rtems_build_name( 'I', 'N', 'T', 'R' )
0244       );
0245     }
0246 
0247     Stack_check_Dope_stack( stack );
0248   }
0249 
0250 #if defined(RTEMS_SMP)
0251   _Thread_Dispatch_enable( cpu_self );
0252 #endif
0253 }
0254 
0255 /*
0256  *  rtems_stack_checker_reporter_print_details
0257  *
0258  *  Report a blown stack. Needs to be a separate routine
0259  *  so that interrupt handlers can use this too.
0260  *
0261  *  NOTE: The system is in a questionable state... we may not get
0262  *        the following message out.
0263  */
0264 void rtems_stack_checker_reporter_print_details(
0265   const Thread_Control *running,
0266   bool pattern_ok
0267 )
0268 {
0269   const Stack_Control *stack = &running->Start.Initial_stack;
0270   void                *pattern_area = Stack_check_Get_pattern(stack);
0271   char                 name[2 * THREAD_DEFAULT_MAXIMUM_NAME_SIZE];
0272 
0273   printk("BLOWN STACK!!!\n");
0274   printk("task control block: 0x%08" PRIxPTR "\n", (intptr_t) running);
0275   printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
0276   printk(
0277     "task name: 0x%08" PRIx32 "\n",
0278     running->Object.name.name_u32
0279   );
0280   _Thread_Get_name(running, name, sizeof(name));
0281   printk("task name string: %s\n", name);
0282   printk(
0283     "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
0284     (unsigned long) stack->size,
0285     (intptr_t) stack->area,
0286     (intptr_t) ((char *) stack->area + stack->size)
0287   );
0288   if (!pattern_ok) {
0289     printk(
0290       "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
0291       (unsigned long) SANITY_PATTERN_SIZE_BYTES,
0292       (intptr_t) pattern_area,
0293       (intptr_t) (pattern_area + SANITY_PATTERN_SIZE_BYTES)
0294     );
0295   }
0296 
0297   #if defined(RTEMS_MULTIPROCESSING)
0298     if (rtems_configuration_get_user_multiprocessing_table()) {
0299       printk(
0300         "node: 0x%08" PRIxPTR "\n",
0301           (intptr_t) rtems_configuration_get_user_multiprocessing_table()->node
0302       );
0303     }
0304   #endif
0305 
0306   rtems_fatal(
0307     RTEMS_FATAL_SOURCE_STACK_CHECKER,
0308     running->Object.name.name_u32
0309   );
0310 }
0311 
0312 void rtems_stack_checker_reporter_quiet(
0313   const Thread_Control *running,
0314   bool pattern_ok
0315 )
0316 {
0317   rtems_fatal(
0318     RTEMS_FATAL_SOURCE_STACK_CHECKER,
0319     running->Object.name.name_u32
0320     );
0321 }
0322 
0323 /*
0324  *  rtems_stack_checker_switch_extension
0325  */
0326 void rtems_stack_checker_switch_extension(
0327   Thread_Control *running,
0328   Thread_Control *heir
0329 )
0330 {
0331   bool sp_ok;
0332   bool pattern_ok;
0333   const Stack_Control *stack;
0334 
0335   /*
0336    *  Check for an out of bounds stack pointer or an overwrite
0337    */
0338 #if defined(RTEMS_SMP)
0339   sp_ok = Stack_check_Frame_pointer_in_range( heir );
0340 
0341   if ( !sp_ok ) {
0342     pattern_ok = Stack_check_Is_sanity_pattern_valid(
0343       &heir->Start.Initial_stack
0344     );
0345     Stack_checker_Reporter( heir, pattern_ok );
0346   }
0347 
0348   pattern_ok = Stack_check_Is_sanity_pattern_valid( &running->Start.Initial_stack );
0349 
0350   if ( !pattern_ok ) {
0351     Stack_checker_Reporter( running, pattern_ok );
0352   }
0353 #else
0354   sp_ok = Stack_check_Frame_pointer_in_range( running );
0355 
0356   pattern_ok = Stack_check_Is_sanity_pattern_valid( &running->Start.Initial_stack );
0357 
0358   if ( !sp_ok || !pattern_ok ) {
0359     Stack_checker_Reporter( running, pattern_ok );
0360   }
0361 #endif
0362 
0363   stack = &Stack_check_Interrupt_stack[ _SMP_Get_current_processor() ];
0364 
0365   if ( stack->area != NULL && !Stack_check_Is_sanity_pattern_valid( stack ) ) {
0366     rtems_fatal(
0367       RTEMS_FATAL_SOURCE_STACK_CHECKER,
0368       rtems_build_name( 'I', 'N', 'T', 'R' )
0369     );
0370   }
0371 }
0372 
0373 /*
0374  *  Check if blown
0375  */
0376 bool rtems_stack_checker_is_blown( void )
0377 {
0378   Thread_Control *executing;
0379 
0380   executing = _Thread_Get_executing();
0381   rtems_stack_checker_switch_extension( executing, executing );
0382 
0383   /*
0384    * The Stack Pointer and the Pattern Area are OK so return false.
0385    */
0386   return false;
0387 }
0388 
0389 /*
0390  * Stack_check_find_high_water_mark
0391  */
0392 static inline void *Stack_check_Find_high_water_mark(
0393   const void *s,
0394   size_t      n
0395 )
0396 {
0397   const uint32_t   *base, *ebase;
0398   uint32_t   length;
0399 
0400   base = s;
0401   length = n/4;
0402 
0403   #if ( CPU_STACK_GROWS_UP == TRUE )
0404     /*
0405      * start at higher memory and find first word that does not
0406      * match pattern
0407      */
0408 
0409     base += length - 1;
0410     for (ebase = s; base > ebase; base--)
0411       if (*base != U32_PATTERN)
0412         return (void *) base;
0413   #else
0414     /*
0415      * start at lower memory and find first word that does not
0416      * match pattern
0417      */
0418 
0419     for (ebase = base + length; base < ebase; base++)
0420       if (*base != U32_PATTERN)
0421         return (void *) base;
0422   #endif
0423 
0424   return NULL;
0425 }
0426 
0427 static void Stack_check_Visit_stack(
0428   const Stack_Control        *stack,
0429   const void                 *current,
0430   const char                 *name,
0431   rtems_id                    id,
0432   rtems_stack_checker_visitor visit,
0433   void                        *arg
0434 )
0435 {
0436   rtems_stack_checker_info info;
0437 
0438   /* This is likely to occur if the stack checker is not actually enabled */
0439   if ( stack->area == NULL ) {
0440     return;
0441   }
0442 
0443   info.id = id;
0444   info.name = name;
0445   info.current = current;
0446   info.begin  = Stack_check_Usable_stack_start( stack );
0447   info.size = Stack_check_Usable_stack_size( stack );
0448 
0449   if ( Stack_check_Initialized ) {
0450     void *high_water_mark;
0451 
0452     high_water_mark =
0453       Stack_check_Find_high_water_mark( info.begin, info.size );
0454 
0455     if ( high_water_mark != NULL ) {
0456       info.used =
0457         Stack_check_Calculate_used( info.begin, info.size, high_water_mark );
0458     } else {
0459       info.used = 0;
0460     }
0461   } else {
0462     info.used = UINTPTR_MAX;
0463   }
0464 
0465   ( *visit )( &info, arg );
0466 }
0467 
0468 typedef struct {
0469   rtems_stack_checker_visitor visit;
0470   void *arg;
0471 } Stack_check_Visitor;
0472 
0473 static bool Stack_check_Visit_thread(
0474   Thread_Control *the_thread,
0475   void           *arg
0476 )
0477 {
0478   Stack_check_Visitor *visitor;
0479   char                 name[ 22 ];
0480   uintptr_t sp = (uintptr_t) _CPU_Context_Get_SP( &the_thread->Registers );
0481 
0482   visitor = arg;
0483   _Thread_Get_name( the_thread, name, sizeof( name ) );
0484   Stack_check_Visit_stack(
0485     &the_thread->Start.Initial_stack,
0486     (void *) sp,
0487     name,
0488     the_thread->Object.id,
0489     visitor->visit,
0490     visitor->arg
0491   );
0492   return false;
0493 }
0494 
0495 static void Stack_check_Visit_interrupt_stack(
0496   const Stack_Control        *stack,
0497   uint32_t                    id,
0498   rtems_stack_checker_visitor visit,
0499   void                       *arg
0500 )
0501 {
0502   Stack_check_Visit_stack(
0503     stack,
0504     NULL,
0505     "Interrupt Stack",
0506     id,
0507     visit,
0508     arg
0509   );
0510 }
0511 
0512 static void Stack_check_Print_info(
0513   const rtems_stack_checker_info *info,
0514   void                           *arg
0515 )
0516 {
0517   const rtems_printer *printer;
0518 
0519   printer = arg;
0520 
0521   rtems_printf(
0522     printer,
0523     "0x%08" PRIx32 " %-21s 0x%08" PRIxPTR " 0x%08" PRIxPTR " 0x%08" PRIxPTR " %6" PRIuPTR " ",
0524     info->id,
0525     info->name,
0526     (uintptr_t) info->begin,
0527     (uintptr_t) info->begin + info->size - 1,
0528     (uintptr_t) info->current,
0529     info->size
0530   );
0531 
0532   if ( info->used != UINTPTR_MAX ) {
0533     rtems_printf( printer, "%6" PRIuPTR "\n", info->used );
0534   } else {
0535     rtems_printf( printer, "N/A\n" );
0536   }
0537 }
0538 
0539 void rtems_stack_checker_report_usage_with_plugin(
0540   const rtems_printer* printer
0541 )
0542 {
0543   rtems_printf(
0544      printer,
0545      "                             STACK USAGE BY THREAD\n"
0546      "ID         NAME                  LOW        HIGH       CURRENT     AVAIL   USED\n"
0547   );
0548 
0549   rtems_stack_checker_iterate(
0550     Stack_check_Print_info,
0551     RTEMS_DECONST( rtems_printer *, printer )
0552   );
0553 }
0554 
0555 void rtems_stack_checker_report_usage( void )
0556 {
0557   rtems_printer printer;
0558   rtems_print_printer_printk(&printer);
0559   rtems_stack_checker_report_usage_with_plugin( &printer );
0560 }
0561 
0562 void rtems_stack_checker_iterate( rtems_stack_checker_visitor visit, void *arg )
0563 {
0564   Stack_check_Visitor visitor;
0565   uint32_t            cpu_max;
0566   uint32_t            cpu_index;
0567 
0568   visitor.visit = visit;
0569   visitor.arg = arg;
0570   rtems_task_iterate( Stack_check_Visit_thread, &visitor );
0571 
0572   cpu_max = rtems_scheduler_get_processor_maximum();
0573 
0574   for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
0575     Stack_check_Visit_interrupt_stack(
0576       &Stack_check_Interrupt_stack[ cpu_index ],
0577       cpu_index,
0578       visit,
0579       arg
0580     );
0581   }
0582 }
0583 
0584 static void Stack_check_Prepare_interrupt_stacks( void )
0585 {
0586   Stack_Control stack;
0587   uint32_t      cpu_index;
0588   uint32_t      cpu_max;
0589 
0590   stack.size = rtems_configuration_get_interrupt_stack_size();
0591   stack.area = _ISR_Stack_area_begin;
0592   cpu_max = rtems_configuration_get_maximum_processors();
0593 
0594   for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
0595     Stack_check_Add_sanity_pattern( &stack );
0596     stack.area = _Addresses_Add_offset( stack.area, stack.size );
0597   }
0598 }
0599 
0600 RTEMS_SYSINIT_ITEM(
0601   Stack_check_Prepare_interrupt_stacks,
0602   RTEMS_SYSINIT_ISR_STACK,
0603   RTEMS_SYSINIT_ORDER_MIDDLE
0604 );