source: rtems/cpukit/libmisc/stackchk/check.c @ 7d1acc03

5
Last change on this file since 7d1acc03 was 7d1acc03, checked in by Sebastian Huber <sebastian.huber@…>, on 09/24/18 at 07:15:10

stackchk: Fix interrupt stack preparation

We have to prepare the interrupt stack of each processor.

Update #3459.

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