source: rtems/cpukit/libmisc/stackchk/check.c @ 29fd3878

Last change on this file since 29fd3878 was 29fd3878, checked in by Joel Sherrill <joel.sherrill@…>, on Apr 2, 2007 at 2:34:39 PM

2007-04-02 Joel Sherrill <joel@…>

  • libmisc/stackchk/check.c: Add code to check validity of frame pointer in addition to the pattern area being overwritten. Also do some cleanup.
  • Property mode set to 100644
File size: 11.3 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-2007.
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 * HACK
29 * the stack dump information should be printed by a "fatal" extension.
30 * Fatal extensions only get called via rtems_fatal_error_occurred()
31 * and not when rtems_shutdown_executive() is called.
32 * I hope/think this is changing so that fatal extensions are renamed
33 * to "shutdown" extensions.
34 * When that happens, this #define should be deleted and all the code
35 * it marks.
36 */
37#define DONT_USE_FATAL_EXTENSION
38
39#include <assert.h>
40#include <string.h>
41#include <stdlib.h>
42
43#include <rtems/bspIo.h>
44#include <rtems/stackchk.h>
45#include "internal.h"
46
47/*
48 *  The extension table for the stack checker.
49 */
50
51rtems_extensions_table Stack_check_Extension_table = {
52  Stack_check_Create_extension,     /* rtems_task_create  */
53  0,                                /* rtems_task_start   */
54  0,                                /* rtems_task_restart */
55  0,                                /* rtems_task_delete  */
56  Stack_check_Switch_extension,     /* task_switch  */
57  Stack_check_Begin_extension,      /* task_begin   */
58  0,                                /* task_exitted */
59#ifdef DONT_USE_FATAL_EXTENSION
60  0,                                /* fatal        */
61#else
62  Stack_check_Fatal_extension,      /* fatal        */
63#endif
64};
65
66/*
67 *  The "magic pattern" used to mark the end of the stack.
68 */
69
70Stack_check_Control Stack_check_Pattern;
71
72/*
73 * Helper function to report if the actual stack pointer is in range.
74 *
75 * NOTE: This uses a GCC specific method.
76 */
77
78static inline boolean Stack_check_Frame_pointer_in_range(
79  Stack_Control *the_stack
80)
81{
82  #if defined(__GNUC__)
83    void *sp = __builtin_frame_address(0);
84
85    if ( sp < the_stack->area ) {
86      printk( "Stack Pointer Too Low!\n" );
87      return FALSE;
88    }
89    if ( sp > (the_stack->area + the_stack->size) ) {
90      printk( "Stack Pointer Too High!\n" );
91      return FALSE;
92    }
93  #endif
94  return TRUE;
95}
96
97/*
98 *  Where the pattern goes in the stack area is dependent upon
99 *  whether the stack grow to the high or low area of the memory.
100 *
101 */
102
103#if ( CPU_STACK_GROWS_UP == TRUE )
104
105#define Stack_check_Get_pattern_area( _the_stack ) \
106  ((Stack_check_Control *) ((char *)(_the_stack)->area + \
107       (_the_stack)->size - sizeof( Stack_check_Control ) ))
108
109#define Stack_check_Calculate_used( _low, _size, _high_water ) \
110    ((char *)(_high_water) - (char *)(_low))
111
112#define Stack_check_usable_stack_start(_the_stack) \
113    ((_the_stack)->area)
114
115#else
116
117
118#define Stack_check_Get_pattern_area( _the_stack ) \
119  ((Stack_check_Control *) ((char *)(_the_stack)->area + HEAP_OVERHEAD))
120
121#define Stack_check_Calculate_used( _low, _size, _high_water) \
122    ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
123
124#define Stack_check_usable_stack_start(_the_stack) \
125    ((char *)(_the_stack)->area + sizeof(Stack_check_Control))
126
127#endif
128
129#define Stack_check_usable_stack_size(_the_stack) \
130    ((_the_stack)->size - sizeof(Stack_check_Control))
131
132
133/*
134 * Do we have an interrupt stack?
135 * XXX it would sure be nice if the interrupt stack were also
136 *     stored in a "stack" structure!
137 */
138
139
140Stack_Control stack_check_interrupt_stack;
141
142/*
143 * Prototypes necessary for forward references
144 */
145
146void Stack_check_Dump_usage( void );
147
148/*
149 * Fill an entire stack area with BYTE_PATTERN.
150 * This will be used by a Fatal extension to check for
151 * amount of actual stack used
152 */
153
154void
155stack_check_dope_stack(Stack_Control *stack)
156{
157    memset(stack->area, BYTE_PATTERN, stack->size);
158}
159
160
161/*PAGE
162 *
163 *  Stack_check_Initialize
164 */
165
166static int   stack_check_initialized = 0;
167
168void Stack_check_Initialize( void )
169{
170  uint32_t            *p;
171
172  if (stack_check_initialized)
173    return;
174
175  /*
176   * Dope the pattern and fill areas
177   */
178
179  for ( p = Stack_check_Pattern.pattern;
180        p < &Stack_check_Pattern.pattern[PATTERN_SIZE_WORDS];
181        p += 4
182      )
183  {
184      p[0] = 0xFEEDF00D;          /* FEED FOOD to BAD DOG */
185      p[1] = 0x0BAD0D06;
186      p[2] = 0xDEADF00D;          /* DEAD FOOD GOOD DOG */
187      p[3] = 0x600D0D06;
188  };
189
190  /*
191   * If appropriate, setup the interrupt stack for high water testing
192   * also.
193   */
194#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
195  if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high)
196  {
197      stack_check_interrupt_stack.area = _CPU_Interrupt_stack_low;
198      stack_check_interrupt_stack.size = (char *) _CPU_Interrupt_stack_high -
199                                              (char *) _CPU_Interrupt_stack_low;
200
201      stack_check_dope_stack(&stack_check_interrupt_stack);
202  }
203#endif
204
205#ifdef DONT_USE_FATAL_EXTENSION
206#ifdef RTEMS_DEBUG
207    /*
208     * this would normally be called by a fatal extension
209     * handler, but we don't run fatal extensions unless
210     * we fatal error.
211     */
212  atexit(Stack_check_Dump_usage);
213#endif
214#endif
215
216  stack_check_initialized = 1;
217}
218
219/*PAGE
220 *
221 *  Stack_check_Create_extension
222 */
223
224boolean Stack_check_Create_extension(
225  Thread_Control *running,
226  Thread_Control *the_thread
227)
228{
229    if (!stack_check_initialized)
230      Stack_check_Initialize();
231
232    if (the_thread /* XXX && (the_thread != _Thread_Executing) */ )
233        stack_check_dope_stack(&the_thread->Start.Initial_stack);
234
235    return TRUE;
236}
237
238/*PAGE
239 *
240 *  Stack_check_Begin_extension
241 */
242
243void Stack_check_Begin_extension(
244  Thread_Control *the_thread
245)
246{
247  Stack_check_Control  *the_pattern;
248
249  if (!stack_check_initialized)
250    Stack_check_Initialize();
251
252  if ( the_thread->Object.id == 0 )        /* skip system tasks */
253    return;
254
255  the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
256
257  *the_pattern = Stack_check_Pattern;
258}
259
260/*PAGE
261 *
262 *  Stack_check_report_blown_task
263 *  Report a blown stack.  Needs to be a separate routine
264 *  so that interrupt handlers can use this too.
265 *
266 *  NOTE: The system is in a questionable state... we may not get
267 *        the following message out.
268 */
269
270void Stack_check_report_blown_task(
271  Thread_Control *running,
272  boolean         pattern_ok
273)
274{
275    Stack_Control *stack = &running->Start.Initial_stack;
276
277    printk(
278        "BLOWN STACK!!! Offending task(%p): id=0x%08" PRIx32
279             "; name=0x%08" PRIx32,
280        running,
281        running->Object.id,
282        (uint32_t) running->Object.name
283    );
284
285    if (rtems_configuration_get_user_multiprocessing_table())
286        printk(
287          "; node=%d\n",
288          rtems_configuration_get_user_multiprocessing_table()->node
289       );
290    else
291        printk( "\n");
292
293    printk(
294        "  stack covers range %p - %p (%d bytes)\n",
295        stack->area,
296        stack->area + stack->size - 1,
297        stack->size);
298
299    if ( !pattern_ok ) {
300      printk(
301          "  Damaged pattern begins at 0x%08lx and is %ld bytes long\n",
302          (unsigned long) Stack_check_Get_pattern_area(stack),
303          (long) PATTERN_SIZE_BYTES);
304    }
305
306    rtems_fatal_error_occurred( (uint32_t) "STACK BLOWN" );
307}
308
309/*PAGE
310 *
311 *  Stack_check_Switch_extension
312 */
313
314void Stack_check_Switch_extension(
315  Thread_Control *running,
316  Thread_Control *heir
317)
318{
319  Stack_Control *the_stack = &running->Start.Initial_stack;
320  void          *pattern;
321  boolean        sp_ok;
322  boolean        pattern_ok;
323
324  pattern = (void *) Stack_check_Get_pattern_area(the_stack)->pattern;
325
326
327  /*
328   *  Check for an out of bounds stack pointer and then an overwrite
329   */
330
331  sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
332  pattern_ok = (!memcmp( pattern,
333            (void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES));
334
335  if ( !sp_ok || !pattern_ok ) {
336    Stack_check_report_blown_task( running, pattern_ok );
337  }
338}
339
340void *Stack_check_find_high_water_mark(
341  const void *s,
342  size_t n
343)
344{
345  const uint32_t   *base, *ebase;
346  uint32_t   length;
347
348  base = s;
349  length = n/4;
350
351#if ( CPU_STACK_GROWS_UP == TRUE )
352  /*
353   * start at higher memory and find first word that does not
354   * match pattern
355   */
356
357  base += length - 1;
358  for (ebase = s; base > ebase; base--)
359      if (*base != U32_PATTERN)
360          return (void *) base;
361#else
362  /*
363   * start at lower memory and find first word that does not
364   * match pattern
365   */
366
367  base += PATTERN_SIZE_WORDS;
368  for (ebase = base + length; base < ebase; base++)
369      if (*base != U32_PATTERN)
370          return (void *) base;
371#endif
372
373  return (void *)0;
374}
375
376/*PAGE
377 *
378 *  Stack_check_Dump_threads_usage
379 *  Try to print out how much stack was actually used by the task.
380 *
381 */
382
383void Stack_check_Dump_threads_usage(
384  Thread_Control *the_thread
385)
386{
387  uint32_t        size, used;
388  void           *low;
389  void           *high_water_mark;
390  Stack_Control  *stack;
391  uint32_t        u32_name;
392  char            name_str[5];
393  char            *name;
394  Objects_Information *info;
395
396  if ( !the_thread )
397    return;
398
399  /*
400   * XXX HACK to get to interrupt stack
401   */
402
403  if (the_thread == (Thread_Control *) -1)
404  {
405      if (stack_check_interrupt_stack.area)
406      {
407          stack = &stack_check_interrupt_stack;
408          the_thread = 0;
409      }
410      else
411          return;
412  }
413  else
414      stack = &the_thread->Start.Initial_stack;
415
416  low  = Stack_check_usable_stack_start(stack);
417  size = Stack_check_usable_stack_size(stack);
418
419  high_water_mark = Stack_check_find_high_water_mark(low, size);
420
421  if ( high_water_mark )
422    used = Stack_check_Calculate_used( low, size, high_water_mark );
423  else
424    used = 0;
425
426  name = name_str;
427  if ( the_thread ) {
428    info = _Objects_Get_information(the_thread->Object.id);
429    if ( info->is_string ) {
430      name = (char *) the_thread->Object.name;
431    } else {
432      u32_name = (uint32_t) the_thread->Object.name;
433      name[ 0 ] = (u32_name >> 24) & 0xff;
434      name[ 1 ] = (u32_name >> 16) & 0xff;
435      name[ 2 ] = (u32_name >>  8) & 0xff;
436      name[ 3 ] = (u32_name >>  0) & 0xff;
437      name[ 4 ] = '\0';
438    }
439  } else {
440    u32_name = rtems_build_name('I', 'N', 'T', 'R');
441    name[ 0 ] = (u32_name >> 24) & 0xff;
442    name[ 1 ] = (u32_name >> 16) & 0xff;
443    name[ 2 ] = (u32_name >>  8) & 0xff;
444    name[ 3 ] = (u32_name >>  0) & 0xff;
445    name[ 4 ] = '\0';
446  }
447
448  printk("0x%08" PRIx32 "  %4s  %p - %p   %8" PRId32 "   %8" PRId32 "\n",
449          the_thread ? the_thread->Object.id : ~0,
450          name,
451          stack->area,
452          stack->area + stack->size - 1,
453          size,
454          used
455  );
456}
457
458/*PAGE
459 *
460 *  Stack_check_Fatal_extension
461 */
462
463void Stack_check_Fatal_extension(
464    Internal_errors_Source  source,
465    boolean                 is_internal,
466    uint32_t                status
467)
468{
469#ifndef DONT_USE_FATAL_EXTENSION
470    if (status == 0)
471        Stack_check_Dump_usage();
472#endif
473}
474
475
476/*PAGE
477 *
478 *  Stack_check_Dump_usage
479 */
480
481void Stack_check_Dump_usage( void )
482{
483  if (stack_check_initialized == 0)
484      return;
485
486  printk("Stack usage by thread\n");
487  printk(
488    "    ID      NAME       LOW        HIGH     AVAILABLE      USED\n"
489  );
490
491  /* iterate over all threads and dump the usage */
492  rtems_iterate_over_all_threads( Stack_check_Dump_threads_usage );
493
494  /* dump interrupt stack info if any */
495  Stack_check_Dump_threads_usage((Thread_Control *) -1);
496}
Note: See TracBrowser for help on using the repository browser.