source: rtems/cpukit/libmisc/stackchk/check.c @ a12f7e9

4.11
Last change on this file since a12f7e9 was a12f7e9, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 14, 2012 at 2:10:46 PM

score: Add RTEMS_FATAL_SOURCE_STACK_CHECKER

  • Property mode set to 100644
File size: 12.6 KB
Line 
1/*
2 *  Stack Overflow Check User Extension Set
3 *
4 *  NOTE:  This extension set automatically determines at
5 *         initialization time whether the stack for this
6 *         CPU grows up or down and installs the correct
7 *         extension routines for that direction.
8 *
9 *  COPYRIGHT (c) 1989-2010.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  The license and distribution terms for this file may be
13 *  found in the file LICENSE in this distribution or at
14 *  http://www.rtems.com/license/LICENSE.
15 *
16 */
17
18#ifdef HAVE_CONFIG_H
19#include "config.h"
20#endif
21
22#include <rtems.h>
23#include <inttypes.h>
24
25/*
26 * The stack dump information may be printed by a "fatal" extension.
27 * Fatal extensions only get called via rtems_fatal_error_occurred()
28 * and not when rtems_shutdown_executive() is called.
29 * When that happens, this #define should be deleted and all the code
30 * it marks.
31 */
32#define DONT_USE_FATAL_EXTENSION
33
34#include <string.h>
35#include <stdlib.h>
36
37#include <rtems/bspIo.h>
38#include <rtems/stackchk.h>
39#include "internal.h"
40
41/*
42 *  Variable to indicate when the stack checker has been initialized.
43 */
44static int   Stack_check_Initialized = 0;
45
46/*
47 *  The "magic pattern" used to mark the end of the stack.
48 */
49Stack_check_Control Stack_check_Pattern;
50
51/*
52 * Helper function to report if the actual stack pointer is in range.
53 *
54 * NOTE: This uses a GCC specific method.
55 */
56static inline bool Stack_check_Frame_pointer_in_range(
57  Stack_Control *the_stack
58)
59{
60  #if defined(__GNUC__)
61    void *sp = __builtin_frame_address(0);
62
63    if ( sp < the_stack->area ) {
64      return false;
65    }
66    if ( sp > (the_stack->area + the_stack->size) ) {
67      return false;
68    }
69  #else
70    #error "How do I check stack bounds on a non-GNU compiler?"
71  #endif
72  return true;
73}
74
75/*
76 *  Where the pattern goes in the stack area is dependent upon
77 *  whether the stack grow to the high or low area of the memory.
78 */
79#if (CPU_STACK_GROWS_UP == TRUE)
80  #define Stack_check_Get_pattern( _the_stack ) \
81    ((char *)(_the_stack)->area + \
82         (_the_stack)->size - sizeof( Stack_check_Control ) )
83
84  #define Stack_check_Calculate_used( _low, _size, _high_water ) \
85      ((char *)(_high_water) - (char *)(_low))
86
87  #define Stack_check_usable_stack_start(_the_stack) \
88    ((_the_stack)->area)
89
90#else
91  /*
92   * We need this magic offset because during a task delete the task stack will
93   * be freed before we enter the task switch extension which checks the stack.
94   * The task stack free operation will write the next and previous pointers
95   * for the free list into this area.
96   */
97  #define Stack_check_Get_pattern( _the_stack ) \
98    ((char *)(_the_stack)->area + sizeof(Heap_Block) - HEAP_BLOCK_HEADER_SIZE)
99
100  #define Stack_check_Calculate_used( _low, _size, _high_water) \
101      ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
102
103  #define Stack_check_usable_stack_start(_the_stack) \
104      ((char *)(_the_stack)->area + sizeof(Stack_check_Control))
105
106#endif
107
108/*
109 *  Obtain a properly typed pointer to the area to check.
110 */
111#define Stack_check_Get_pattern_area( _the_stack ) \
112  (Stack_check_Control *) Stack_check_Get_pattern( _the_stack )
113
114/*
115 *  The assumption is that if the pattern gets overwritten, the task
116 *  is too close.  This defines the usable stack memory.
117 */
118#define Stack_check_usable_stack_size(_the_stack) \
119    ((_the_stack)->size - sizeof(Stack_check_Control))
120
121#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
122  /*
123   *  Did RTEMS allocate the interrupt stack? If so, put it in
124   *  Stack_Control format.
125   */
126  Stack_Control Stack_check_Interrupt_stack;
127#endif
128
129/*
130 *  Fill an entire stack area with BYTE_PATTERN.  This will be used
131 *  to check for amount of actual stack used.
132 */
133#define Stack_check_Dope_stack(_stack) \
134  memset((_stack)->area, BYTE_PATTERN, (_stack)->size)
135
136/*
137 *  Stack_check_Initialize
138 */
139static void Stack_check_Initialize( void )
140{
141  int       i;
142  uint32_t *p;
143  static    uint32_t pattern[ 4 ] = {
144    0xFEEDF00D, 0x0BAD0D06,  /* FEED FOOD to  BAD DOG */
145    0xDEADF00D, 0x600D0D06   /* DEAD FOOD but GOOD DOG */
146  };
147
148  if ( Stack_check_Initialized )
149    return;
150
151  /*
152   * Dope the pattern and fill areas
153   */
154  p = Stack_check_Pattern.pattern;
155  for ( i = 0; i < PATTERN_SIZE_WORDS; i++ ) {
156      p[i] = pattern[ i%4 ];
157  }
158
159  /*
160   * If appropriate, setup the interrupt stack for high water testing
161   * also.
162   */
163  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
164    if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) {
165      Stack_check_Interrupt_stack.area = _CPU_Interrupt_stack_low;
166      Stack_check_Interrupt_stack.size = (char *) _CPU_Interrupt_stack_high -
167                                  (char *) _CPU_Interrupt_stack_low;
168      Stack_check_Dope_stack(&Stack_check_Interrupt_stack);
169   }
170  #endif
171
172  Stack_check_Initialized = 1;
173}
174
175/*
176 *  rtems_stack_checker_create_extension
177 */
178bool rtems_stack_checker_create_extension(
179  Thread_Control *running __attribute__((unused)),
180  Thread_Control *the_thread
181)
182{
183  Stack_check_Initialize();
184
185  if (the_thread)
186    Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
187
188  return true;
189}
190
191/*
192 *  rtems_stack_checker_Begin_extension
193 */
194void rtems_stack_checker_begin_extension(
195  Thread_Control *the_thread
196)
197{
198  Stack_check_Control  *the_pattern;
199
200  if ( the_thread->Object.id == 0 )        /* skip system tasks */
201    return;
202
203  the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
204
205  *the_pattern = Stack_check_Pattern;
206}
207
208/*
209 *  Stack_check_report_blown_task
210 *
211 *  Report a blown stack.  Needs to be a separate routine
212 *  so that interrupt handlers can use this too.
213 *
214 *  NOTE: The system is in a questionable state... we may not get
215 *        the following message out.
216 */
217void Stack_check_report_blown_task(
218  Thread_Control *running,
219  bool pattern_ok
220) RTEMS_COMPILER_NO_RETURN_ATTRIBUTE;
221
222void Stack_check_report_blown_task(Thread_Control *running, bool pattern_ok)
223{
224  Stack_Control *stack = &running->Start.Initial_stack;
225  void          *pattern_area = Stack_check_Get_pattern(stack);
226  char           name[32];
227
228  printk("BLOWN STACK!!!\n");
229  printk("task control block: 0x%08" PRIxPTR "\n", running);
230  printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
231  printk(
232    "task name: 0x%08" PRIx32 "\n",
233    running->Object.name.name_u32
234  );
235  printk(
236    "task name string: %s\n",
237    rtems_object_get_name(running->Object.id, sizeof(name), name)
238  );
239  printk(
240    "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
241    (unsigned long) stack->size,
242    stack->area,
243    ((char *) stack->area + stack->size)
244  );
245  if (!pattern_ok) {
246    printk(
247      "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
248      (unsigned long) PATTERN_SIZE_BYTES,
249      pattern_area,
250      (pattern_area + PATTERN_SIZE_BYTES)
251    );
252  }
253
254  #if defined(RTEMS_MULTIPROCESSING)
255    if (rtems_configuration_get_user_multiprocessing_table()) {
256      printk(
257        "node: 0x%08" PRIxPTR "\n",
258          rtems_configuration_get_user_multiprocessing_table()->node
259      );
260    }
261  #endif
262
263  rtems_fatal(
264    RTEMS_FATAL_SOURCE_STACK_CHECKER,
265    running->Object.name.name_u32
266  );
267}
268
269/*
270 *  rtems_stack_checker_switch_extension
271 */
272void rtems_stack_checker_switch_extension(
273  Thread_Control *running __attribute__((unused)),
274  Thread_Control *heir __attribute__((unused))
275)
276{
277  Stack_Control *the_stack = &running->Start.Initial_stack;
278  void          *pattern;
279  bool           sp_ok;
280  bool           pattern_ok = true;
281
282  pattern = Stack_check_Get_pattern_area(the_stack);
283
284  /*
285   *  Check for an out of bounds stack pointer or an overwrite
286   */
287  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
288
289  pattern_ok = (!memcmp( pattern,
290            (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES));
291
292  if ( !sp_ok || !pattern_ok ) {
293    Stack_check_report_blown_task( running, pattern_ok );
294  }
295}
296
297/*
298 *  Check if blown
299 */
300bool rtems_stack_checker_is_blown( void )
301{
302  Stack_Control *the_stack = &_Thread_Executing->Start.Initial_stack;
303  bool           sp_ok;
304  bool           pattern_ok = true;
305
306  /*
307   *  Check for an out of bounds stack pointer
308   */
309
310  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
311
312  /*
313   * The stack checker must be initialized before the pattern is there
314   * to check.
315   */
316  if ( Stack_check_Initialized ) {
317    pattern_ok = (!memcmp(
318      Stack_check_Get_pattern(the_stack),
319      (void *) Stack_check_Pattern.pattern,
320      PATTERN_SIZE_BYTES
321    ));
322  }
323
324
325  /*
326   * Let's report as much as we can.
327   */
328  if ( !sp_ok || !pattern_ok ) {
329    Stack_check_report_blown_task( _Thread_Executing, pattern_ok );
330    /* DOES NOT RETURN */
331  }
332
333  /*
334   * The Stack Pointer and the Pattern Area are OK so return false.
335   */
336  return false;
337}
338
339/*
340 * Stack_check_find_high_water_mark
341 */
342static inline void *Stack_check_find_high_water_mark(
343  const void *s,
344  size_t      n
345)
346{
347  const uint32_t   *base, *ebase;
348  uint32_t   length;
349
350  base = s;
351  length = n/4;
352
353  #if ( CPU_STACK_GROWS_UP == TRUE )
354    /*
355     * start at higher memory and find first word that does not
356     * match pattern
357     */
358
359    base += length - 1;
360    for (ebase = s; base > ebase; base--)
361      if (*base != U32_PATTERN)
362        return (void *) base;
363  #else
364    /*
365     * start at lower memory and find first word that does not
366     * match pattern
367     */
368
369    base += PATTERN_SIZE_WORDS;
370    for (ebase = base + length; base < ebase; base++)
371      if (*base != U32_PATTERN)
372        return (void *) base;
373  #endif
374
375  return (void *)0;
376}
377
378/*
379 *  Stack_check_Dump_threads_usage
380 *
381 *  Try to print out how much stack was actually used by the task.
382 */
383static void                   *print_context;
384static rtems_printk_plugin_t   print_handler;
385
386static void Stack_check_Dump_threads_usage(
387  Thread_Control *the_thread
388)
389{
390  uint32_t        size, used;
391  void           *low;
392  void           *high_water_mark;
393  void           *current;
394  Stack_Control  *stack;
395  char            name[5];
396
397  /*
398   *  The pointer passed in for the_thread is guaranteed to be non-NULL from
399   *  rtems_iterate_over_all_threads() so no need to check it here.
400   */
401
402  /*
403   *  Obtain interrupt stack information
404   */
405  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
406    if (the_thread == (Thread_Control *) -1) {
407      if (!Stack_check_Interrupt_stack.area)
408        return;
409      stack = &Stack_check_Interrupt_stack;
410      the_thread = 0;
411      current = 0;
412    } else 
413  #endif
414    {
415      stack  = &the_thread->Start.Initial_stack;
416      current = (void *)_CPU_Context_Get_SP( &the_thread->Registers );
417    }
418
419  low  = Stack_check_usable_stack_start(stack);
420  size = Stack_check_usable_stack_size(stack);
421
422  high_water_mark = Stack_check_find_high_water_mark(low, size);
423
424  if ( high_water_mark )
425    used = Stack_check_Calculate_used( low, size, high_water_mark );
426  else
427    used = 0;
428
429
430  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
431    if ( the_thread )
432  #endif
433    {
434      (*print_handler)(
435        print_context,
436        "0x%08" PRIx32 "  %4s",
437        the_thread->Object.id,
438        rtems_object_get_name( the_thread->Object.id, sizeof(name), name )
439      );
440    }
441    #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
442      else {
443        (*print_handler)( print_context, "0x%08" PRIx32 "  INTR", ~0 );
444      }
445    #endif
446
447  (*print_handler)(
448    print_context,
449    " %010p - %010p %010p  %8" PRId32 "   ",
450    stack->area,
451    stack->area + stack->size - 1,
452    current,
453    size
454  );
455
456  if (Stack_check_Initialized == 0) {
457    (*print_handler)( print_context, "Unavailable\n" );
458  } else {
459    (*print_handler)( print_context, "%8" PRId32 "\n", used );
460  }
461
462
463}
464
465/*
466 *  rtems_stack_checker_fatal_extension
467 */
468#ifndef DONT_USE_FATAL_EXTENSION
469  void rtems_stack_checker_fatal_extension(
470    Internal_errors_Source  source,
471    bool                    is_internal,
472    uint32_t                status
473  )
474  {
475    if (status == 0)
476      rtems_stack_checker_report_usage();
477  }
478#endif
479
480/*
481 *  rtems_stack_checker_report_usage
482 */
483
484void rtems_stack_checker_report_usage_with_plugin(
485  void                  *context,
486  rtems_printk_plugin_t  print
487)
488{
489  if ( !print )
490    return;
491
492  print_context = context;
493  print_handler = print;
494
495  (*print)( context, "Stack usage by thread\n");
496  (*print)( context,
497"    ID      NAME    LOW          HIGH     CURRENT     AVAILABLE     USED\n"
498  );
499
500  /* iterate over all threads and dump the usage */
501  rtems_iterate_over_all_threads( Stack_check_Dump_threads_usage );
502
503  #if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
504    /* dump interrupt stack info if any */
505    Stack_check_Dump_threads_usage((Thread_Control *) -1);
506  #endif
507
508  print_context = NULL;
509  print_handler = NULL;
510}
511
512void rtems_stack_checker_report_usage( void )
513{
514  rtems_stack_checker_report_usage_with_plugin( NULL, printk_plugin );
515}
Note: See TracBrowser for help on using the repository browser.