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

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