source: rtems/cpukit/libmisc/stackchk/check.c @ 8bb9d41

5
Last change on this file since 8bb9d41 was 8bb9d41, checked in by Sebastian Huber <sebastian.huber@…>, on 09/07/16 at 12:08:06

stackchk: Use a const pattern to check

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