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

5
Last change on this file since c47ad8e was c47ad8e, checked in by Sebastian Huber <sebastian.huber@…>, on 06/20/18 at 10:03:54

stackchk: Add SMP support

Check the interrupt stacks of all processors. Set up the interrupt
stack of the current processor for high water testing in the thread
begin extension. This must be done after multi-threading started, since
the initialization stacks may reuse the interrupt stacks. Disable
thread dispatching in SMP configurations to prevent thread migration.
Writing to the interrupt stack is only safe if done from the
corresponding processor in thread context.

Update #3459.

  • Property mode set to 100644
File size: 11.5 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#include <string.h>
31#include <stdlib.h>
32
33#include <rtems/bspIo.h>
34#include <rtems/printer.h>
35#include <rtems/stackchk.h>
36#include <rtems/score/percpu.h>
37#include <rtems/score/threadimpl.h>
38
39/*
40 *  This structure is used to fill in and compare the "end of stack"
41 *  marker pattern.
42 *  pattern area must be a multiple of 4 words.
43 */
44
45#if !defined(CPU_STACK_CHECK_PATTERN_INITIALIZER)
46#define CPU_STACK_CHECK_PATTERN_INITIALIZER \
47  { \
48    0xFEEDF00D, 0x0BAD0D06, /* FEED FOOD to  BAD DOG */ \
49    0xDEADF00D, 0x600D0D06  /* DEAD FOOD but GOOD DOG */ \
50  }
51#endif
52
53/*
54 *  The pattern used to fill the entire stack.
55 */
56
57#define BYTE_PATTERN 0xA5
58#define U32_PATTERN 0xA5A5A5A5
59
60/*
61 *  Variable to indicate when the stack checker has been initialized.
62 */
63static bool Stack_check_Initialized;
64
65/*
66 *  The "magic pattern" used to mark the end of the stack.
67 */
68static const uint32_t Stack_check_Pattern[] =
69  CPU_STACK_CHECK_PATTERN_INITIALIZER;
70
71#define PATTERN_SIZE_BYTES sizeof(Stack_check_Pattern)
72
73#define PATTERN_SIZE_WORDS RTEMS_ARRAY_SIZE(Stack_check_Pattern)
74
75/*
76 * Helper function to report if the actual stack pointer is in range.
77 *
78 * NOTE: This uses a GCC specific method.
79 */
80static inline bool Stack_check_Frame_pointer_in_range(
81  const Thread_Control *the_thread
82)
83{
84  #if defined(__GNUC__)
85    void *sp = __builtin_frame_address(0);
86    const Stack_Control *the_stack = &the_thread->Start.Initial_stack;
87
88    if ( sp < the_stack->area ) {
89      return false;
90    }
91    if ( sp > (the_stack->area + the_stack->size) ) {
92      return false;
93    }
94  #else
95    #error "How do I check stack bounds on a non-GNU compiler?"
96  #endif
97  return true;
98}
99
100/*
101 *  Where the pattern goes in the stack area is dependent upon
102 *  whether the stack grow to the high or low area of the memory.
103 */
104#if (CPU_STACK_GROWS_UP == TRUE)
105  #define Stack_check_Get_pattern( _the_stack ) \
106    ((char *)(_the_stack)->area + \
107         (_the_stack)->size - PATTERN_SIZE_BYTES )
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   * We need this magic offset because during a task delete the task stack will
118   * be freed before we enter the task switch extension which checks the stack.
119   * The task stack free operation will write the next and previous pointers
120   * for the free list into this area.
121   */
122  #define Stack_check_Get_pattern( _the_stack ) \
123    ((char *)(_the_stack)->area + sizeof(Heap_Block) - HEAP_BLOCK_HEADER_SIZE)
124
125  #define Stack_check_Calculate_used( _low, _size, _high_water) \
126      ( ((char *)(_low) + (_size)) - (char *)(_high_water) )
127
128  #define Stack_check_usable_stack_start(_the_stack) \
129      ((char *)(_the_stack)->area + PATTERN_SIZE_BYTES)
130
131#endif
132
133/*
134 *  The assumption is that if the pattern gets overwritten, the task
135 *  is too close.  This defines the usable stack memory.
136 */
137#define Stack_check_usable_stack_size(_the_stack) \
138    ((_the_stack)->size - PATTERN_SIZE_BYTES)
139
140#if defined(RTEMS_SMP)
141static Stack_Control Stack_check_Interrupt_stack[ CPU_MAXIMUM_PROCESSORS ];
142#else
143static Stack_Control Stack_check_Interrupt_stack[ 1 ];
144#endif
145
146/*
147 *  Fill an entire stack area with BYTE_PATTERN.  This will be used
148 *  to check for amount of actual stack used.
149 */
150#define Stack_check_Dope_stack(_stack) \
151  memset((_stack)->area, BYTE_PATTERN, (_stack)->size)
152
153static bool Stack_check_Is_pattern_valid(const Thread_Control *the_thread)
154{
155  return memcmp(
156    Stack_check_Get_pattern(&the_thread->Start.Initial_stack),
157    Stack_check_Pattern,
158    PATTERN_SIZE_BYTES
159  ) == 0;
160}
161
162/*
163 *  rtems_stack_checker_create_extension
164 */
165bool rtems_stack_checker_create_extension(
166  Thread_Control *running RTEMS_UNUSED,
167  Thread_Control *the_thread
168)
169{
170  Stack_check_Initialized = true;
171
172  Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
173  memcpy(
174    Stack_check_Get_pattern(&the_thread->Start.Initial_stack),
175    Stack_check_Pattern,
176    PATTERN_SIZE_BYTES
177  );
178
179  return true;
180}
181
182void rtems_stack_checker_begin_extension( Thread_Control *executing )
183{
184#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
185  Per_CPU_Control *cpu_self;
186  uint32_t         cpu_self_index;
187  Stack_Control   *stack;
188
189  /*
190   * If appropriate, set up the interrupt stack of the current processor for
191   * high water testing also.  This must be done after multi-threading started,
192   * since the initialization stacks may reuse the interrupt stacks.  Disable
193   * thread dispatching in SMP configurations to prevent thread migration.
194   * Writing to the interrupt stack is only safe if done from the corresponding
195   * processor in thread context.
196   */
197
198#if defined(RTEMS_SMP)
199  cpu_self = _Thread_Dispatch_disable();
200#else
201  cpu_self = _Per_CPU_Get();
202#endif
203
204  cpu_self_index = _Per_CPU_Get_index( cpu_self );
205  stack = &Stack_check_Interrupt_stack[ cpu_self_index ];
206
207  if ( stack->area == NULL ) {
208    stack->area = cpu_self->interrupt_stack_low;
209    stack->size = (size_t) ( (char *) cpu_self->interrupt_stack_high -
210      (char *) cpu_self->interrupt_stack_low );
211    Stack_check_Dope_stack( stack );
212  }
213
214#if defined(RTEMS_SMP)
215  _Thread_Dispatch_enable( cpu_self );
216#endif
217#endif
218}
219
220/*
221 *  Stack_check_report_blown_task
222 *
223 *  Report a blown stack.  Needs to be a separate routine
224 *  so that interrupt handlers can use this too.
225 *
226 *  NOTE: The system is in a questionable state... we may not get
227 *        the following message out.
228 */
229static void Stack_check_report_blown_task(
230  const Thread_Control *running,
231  bool pattern_ok
232)
233{
234  const Stack_Control *stack = &running->Start.Initial_stack;
235  void                *pattern_area = Stack_check_Get_pattern(stack);
236  char                 name[32];
237
238  printk("BLOWN STACK!!!\n");
239  printk("task control block: 0x%08" PRIxPTR "\n", (intptr_t) running);
240  printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
241  printk(
242    "task name: 0x%08" PRIx32 "\n",
243    running->Object.name.name_u32
244  );
245  _Thread_Get_name(running, name, sizeof(name));
246  printk("task name string: %s\n", name);
247  printk(
248    "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
249    (unsigned long) stack->size,
250    (intptr_t) stack->area,
251    (intptr_t) ((char *) stack->area + stack->size)
252  );
253  if (!pattern_ok) {
254    printk(
255      "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
256      (unsigned long) PATTERN_SIZE_BYTES,
257      (intptr_t) pattern_area,
258      (intptr_t) (pattern_area + PATTERN_SIZE_BYTES)
259    );
260  }
261
262  #if defined(RTEMS_MULTIPROCESSING)
263    if (rtems_configuration_get_user_multiprocessing_table()) {
264      printk(
265        "node: 0x%08" PRIxPTR "\n",
266          (intptr_t) rtems_configuration_get_user_multiprocessing_table()->node
267      );
268    }
269  #endif
270
271  rtems_fatal(
272    RTEMS_FATAL_SOURCE_STACK_CHECKER,
273    running->Object.name.name_u32
274  );
275}
276
277/*
278 *  rtems_stack_checker_switch_extension
279 */
280void rtems_stack_checker_switch_extension(
281  Thread_Control *running RTEMS_UNUSED,
282  Thread_Control *heir RTEMS_UNUSED
283)
284{
285  bool sp_ok;
286  bool pattern_ok;
287
288  /*
289   *  Check for an out of bounds stack pointer or an overwrite
290   */
291  sp_ok = Stack_check_Frame_pointer_in_range( running );
292
293  pattern_ok = Stack_check_Is_pattern_valid( running );
294
295  if ( !sp_ok || !pattern_ok ) {
296    Stack_check_report_blown_task( running, pattern_ok );
297  }
298}
299
300/*
301 *  Check if blown
302 */
303bool rtems_stack_checker_is_blown( void )
304{
305  rtems_stack_checker_switch_extension( _Thread_Get_executing(), NULL );
306
307  /*
308   * The Stack Pointer and the Pattern Area are OK so return false.
309   */
310  return false;
311}
312
313/*
314 * Stack_check_find_high_water_mark
315 */
316static inline void *Stack_check_find_high_water_mark(
317  const void *s,
318  size_t      n
319)
320{
321  const uint32_t   *base, *ebase;
322  uint32_t   length;
323
324  base = s;
325  length = n/4;
326
327  #if ( CPU_STACK_GROWS_UP == TRUE )
328    /*
329     * start at higher memory and find first word that does not
330     * match pattern
331     */
332
333    base += length - 1;
334    for (ebase = s; base > ebase; base--)
335      if (*base != U32_PATTERN)
336        return (void *) base;
337  #else
338    /*
339     * start at lower memory and find first word that does not
340     * match pattern
341     */
342
343    base += PATTERN_SIZE_WORDS;
344    for (ebase = base + length; base < ebase; base++)
345      if (*base != U32_PATTERN)
346        return (void *) base;
347  #endif
348
349  return (void *)0;
350}
351
352static bool Stack_check_Dump_stack_usage(
353  const Stack_Control *stack,
354  const void          *current,
355  const char          *name,
356  uint32_t             id,
357  const rtems_printer *printer
358)
359{
360  uint32_t  size;
361  uint32_t  used;
362  void     *low;
363  void     *high_water_mark;
364
365  low  = Stack_check_usable_stack_start(stack);
366  size = Stack_check_usable_stack_size(stack);
367
368  high_water_mark = Stack_check_find_high_water_mark(low, size);
369
370  if ( high_water_mark )
371    used = Stack_check_Calculate_used( low, size, high_water_mark );
372  else
373    used = 0;
374
375  rtems_printf(
376    printer,
377    "0x%08" PRIx32 " %-21s 0x%08" PRIxPTR " 0x%08" PRIxPTR " 0x%08" PRIxPTR " %6" PRId32 " ",
378    id,
379    name,
380    (uintptr_t) stack->area,
381    (uintptr_t) stack->area + (uintptr_t) stack->size - 1,
382    (uintptr_t) current,
383    size
384  );
385
386  if (Stack_check_Initialized) {
387    rtems_printf( printer, "%6" PRId32 "\n", used );
388  } else {
389    rtems_printf( printer, "N/A\n" );
390  }
391
392  return false;
393}
394
395static bool Stack_check_Dump_threads_usage(
396  Thread_Control *the_thread,
397  void           *arg
398)
399{
400  char                 name[ 22 ];
401  const rtems_printer *printer;
402
403  printer = arg;
404  _Thread_Get_name( the_thread, name, sizeof( name ) );
405  Stack_check_Dump_stack_usage(
406    &the_thread->Start.Initial_stack,
407    (void *) _CPU_Context_Get_SP( &the_thread->Registers ),
408    name,
409    the_thread->Object.id,
410    printer
411  );
412  return false;
413}
414
415static void Stack_check_Dump_interrupt_stack_usage(
416  const Stack_Control *stack,
417  uint32_t             id,
418  const rtems_printer *printer
419)
420{
421  Stack_check_Dump_stack_usage(
422    stack,
423    NULL,
424    "Interrupt Stack",
425    id,
426    printer
427  );
428}
429
430/*
431 *  rtems_stack_checker_report_usage
432 */
433
434void rtems_stack_checker_report_usage_with_plugin(
435  const rtems_printer* printer
436)
437{
438  uint32_t cpu_max;
439  uint32_t cpu_index;
440
441  rtems_printf(
442     printer,
443     "                             STACK USAGE BY THREAD\n"
444     "ID         NAME                  LOW        HIGH       CURRENT     AVAIL   USED\n"
445  );
446
447  /* iterate over all threads and dump the usage */
448  rtems_task_iterate(
449    Stack_check_Dump_threads_usage,
450    RTEMS_DECONST( rtems_printer *, printer )
451  );
452
453  cpu_max = rtems_get_processor_count();
454
455  for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
456    Stack_check_Dump_interrupt_stack_usage(
457      &Stack_check_Interrupt_stack[ cpu_index ],
458      cpu_index,
459      printer
460    );
461  }
462}
463
464void rtems_stack_checker_report_usage( void )
465{
466  rtems_printer printer;
467  rtems_print_printer_printk(&printer);
468  rtems_stack_checker_report_usage_with_plugin( &printer );
469}
Note: See TracBrowser for help on using the repository browser.