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

4.115
Last change on this file since d4a97df3 was d4a97df3, checked in by Joel Sherrill <joel.sherrill@…>, on 08/25/10 at 20:15:56

2010-08-25 Joel Sherrill <joel.sherrill@…>

Coverity Id 163

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