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

5
Last change on this file since a75a7d3 was a75a7d3, checked in by Sebastian Huber <sebastian.huber@…>, on 09/21/18 at 05:30:44

stackchk: Remove heap hack

It is now guranteed that threads do not use a freed stack during
termination.

  • Property mode set to 100644
File size: 11.1 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  #define Stack_check_Get_pattern( _the_stack ) \
117    ((char *)(_the_stack)->area)
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 + PATTERN_SIZE_BYTES)
124
125#endif
126
127/*
128 *  The assumption is that if the pattern gets overwritten, the task
129 *  is too close.  This defines the usable stack memory.
130 */
131#define Stack_check_usable_stack_size(_the_stack) \
132    ((_the_stack)->size - PATTERN_SIZE_BYTES)
133
134#if defined(RTEMS_SMP)
135static Stack_Control Stack_check_Interrupt_stack[ CPU_MAXIMUM_PROCESSORS ];
136#else
137static Stack_Control Stack_check_Interrupt_stack[ 1 ];
138#endif
139
140/*
141 *  Fill an entire stack area with BYTE_PATTERN.  This will be used
142 *  to check for amount of actual stack used.
143 */
144#define Stack_check_Dope_stack(_stack) \
145  memset((_stack)->area, BYTE_PATTERN, (_stack)->size)
146
147static bool Stack_check_Is_pattern_valid(const Thread_Control *the_thread)
148{
149  return memcmp(
150    Stack_check_Get_pattern(&the_thread->Start.Initial_stack),
151    Stack_check_Pattern,
152    PATTERN_SIZE_BYTES
153  ) == 0;
154}
155
156/*
157 *  rtems_stack_checker_create_extension
158 */
159bool rtems_stack_checker_create_extension(
160  Thread_Control *running RTEMS_UNUSED,
161  Thread_Control *the_thread
162)
163{
164  Stack_check_Initialized = true;
165
166  Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
167  memcpy(
168    Stack_check_Get_pattern(&the_thread->Start.Initial_stack),
169    Stack_check_Pattern,
170    PATTERN_SIZE_BYTES
171  );
172
173  return true;
174}
175
176void rtems_stack_checker_begin_extension( Thread_Control *executing )
177{
178  Per_CPU_Control *cpu_self;
179  uint32_t         cpu_self_index;
180  Stack_Control   *stack;
181
182  /*
183   * If appropriate, set up the interrupt stack of the current processor for
184   * high water testing also.  This must be done after multi-threading started,
185   * since the initialization stacks may reuse the interrupt stacks.  Disable
186   * thread dispatching in SMP configurations to prevent thread migration.
187   * Writing to the interrupt stack is only safe if done from the corresponding
188   * processor in thread context.
189   */
190
191#if defined(RTEMS_SMP)
192  cpu_self = _Thread_Dispatch_disable();
193#else
194  cpu_self = _Per_CPU_Get();
195#endif
196
197  cpu_self_index = _Per_CPU_Get_index( cpu_self );
198  stack = &Stack_check_Interrupt_stack[ cpu_self_index ];
199
200  if ( stack->area == NULL ) {
201    stack->area = cpu_self->interrupt_stack_low;
202    stack->size = (size_t) ( (char *) cpu_self->interrupt_stack_high -
203      (char *) cpu_self->interrupt_stack_low );
204    Stack_check_Dope_stack( stack );
205  }
206
207#if defined(RTEMS_SMP)
208  _Thread_Dispatch_enable( cpu_self );
209#endif
210}
211
212/*
213 *  Stack_check_report_blown_task
214 *
215 *  Report a blown stack.  Needs to be a separate routine
216 *  so that interrupt handlers can use this too.
217 *
218 *  NOTE: The system is in a questionable state... we may not get
219 *        the following message out.
220 */
221static void Stack_check_report_blown_task(
222  const Thread_Control *running,
223  bool pattern_ok
224)
225{
226  const Stack_Control *stack = &running->Start.Initial_stack;
227  void                *pattern_area = Stack_check_Get_pattern(stack);
228  char                 name[32];
229
230  printk("BLOWN STACK!!!\n");
231  printk("task control block: 0x%08" PRIxPTR "\n", (intptr_t) running);
232  printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
233  printk(
234    "task name: 0x%08" PRIx32 "\n",
235    running->Object.name.name_u32
236  );
237  _Thread_Get_name(running, name, sizeof(name));
238  printk("task name string: %s\n", name);
239  printk(
240    "task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
241    (unsigned long) stack->size,
242    (intptr_t) stack->area,
243    (intptr_t) ((char *) stack->area + stack->size)
244  );
245  if (!pattern_ok) {
246    printk(
247      "damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
248      (unsigned long) PATTERN_SIZE_BYTES,
249      (intptr_t) pattern_area,
250      (intptr_t) (pattern_area + PATTERN_SIZE_BYTES)
251    );
252  }
253
254  #if defined(RTEMS_MULTIPROCESSING)
255    if (rtems_configuration_get_user_multiprocessing_table()) {
256      printk(
257        "node: 0x%08" PRIxPTR "\n",
258          (intptr_t) rtems_configuration_get_user_multiprocessing_table()->node
259      );
260    }
261  #endif
262
263  rtems_fatal(
264    RTEMS_FATAL_SOURCE_STACK_CHECKER,
265    running->Object.name.name_u32
266  );
267}
268
269/*
270 *  rtems_stack_checker_switch_extension
271 */
272void rtems_stack_checker_switch_extension(
273  Thread_Control *running RTEMS_UNUSED,
274  Thread_Control *heir RTEMS_UNUSED
275)
276{
277  bool sp_ok;
278  bool pattern_ok;
279
280  /*
281   *  Check for an out of bounds stack pointer or an overwrite
282   */
283  sp_ok = Stack_check_Frame_pointer_in_range( running );
284
285  pattern_ok = Stack_check_Is_pattern_valid( running );
286
287  if ( !sp_ok || !pattern_ok ) {
288    Stack_check_report_blown_task( running, pattern_ok );
289  }
290}
291
292/*
293 *  Check if blown
294 */
295bool rtems_stack_checker_is_blown( void )
296{
297  rtems_stack_checker_switch_extension( _Thread_Get_executing(), NULL );
298
299  /*
300   * The Stack Pointer and the Pattern Area are OK so return false.
301   */
302  return false;
303}
304
305/*
306 * Stack_check_find_high_water_mark
307 */
308static inline void *Stack_check_find_high_water_mark(
309  const void *s,
310  size_t      n
311)
312{
313  const uint32_t   *base, *ebase;
314  uint32_t   length;
315
316  base = s;
317  length = n/4;
318
319  #if ( CPU_STACK_GROWS_UP == TRUE )
320    /*
321     * start at higher memory and find first word that does not
322     * match pattern
323     */
324
325    base += length - 1;
326    for (ebase = s; base > ebase; base--)
327      if (*base != U32_PATTERN)
328        return (void *) base;
329  #else
330    /*
331     * start at lower memory and find first word that does not
332     * match pattern
333     */
334
335    base += PATTERN_SIZE_WORDS;
336    for (ebase = base + length; base < ebase; base++)
337      if (*base != U32_PATTERN)
338        return (void *) base;
339  #endif
340
341  return (void *)0;
342}
343
344static bool Stack_check_Dump_stack_usage(
345  const Stack_Control *stack,
346  const void          *current,
347  const char          *name,
348  uint32_t             id,
349  const rtems_printer *printer
350)
351{
352  uint32_t  size;
353  uint32_t  used;
354  void     *low;
355  void     *high_water_mark;
356
357  low  = Stack_check_usable_stack_start(stack);
358  size = Stack_check_usable_stack_size(stack);
359
360  high_water_mark = Stack_check_find_high_water_mark(low, size);
361
362  if ( high_water_mark )
363    used = Stack_check_Calculate_used( low, size, high_water_mark );
364  else
365    used = 0;
366
367  rtems_printf(
368    printer,
369    "0x%08" PRIx32 " %-21s 0x%08" PRIxPTR " 0x%08" PRIxPTR " 0x%08" PRIxPTR " %6" PRId32 " ",
370    id,
371    name,
372    (uintptr_t) stack->area,
373    (uintptr_t) stack->area + (uintptr_t) stack->size - 1,
374    (uintptr_t) current,
375    size
376  );
377
378  if (Stack_check_Initialized) {
379    rtems_printf( printer, "%6" PRId32 "\n", used );
380  } else {
381    rtems_printf( printer, "N/A\n" );
382  }
383
384  return false;
385}
386
387static bool Stack_check_Dump_threads_usage(
388  Thread_Control *the_thread,
389  void           *arg
390)
391{
392  char                 name[ 22 ];
393  const rtems_printer *printer;
394
395  printer = arg;
396  _Thread_Get_name( the_thread, name, sizeof( name ) );
397  Stack_check_Dump_stack_usage(
398    &the_thread->Start.Initial_stack,
399    (void *) _CPU_Context_Get_SP( &the_thread->Registers ),
400    name,
401    the_thread->Object.id,
402    printer
403  );
404  return false;
405}
406
407static void Stack_check_Dump_interrupt_stack_usage(
408  const Stack_Control *stack,
409  uint32_t             id,
410  const rtems_printer *printer
411)
412{
413  Stack_check_Dump_stack_usage(
414    stack,
415    NULL,
416    "Interrupt Stack",
417    id,
418    printer
419  );
420}
421
422/*
423 *  rtems_stack_checker_report_usage
424 */
425
426void rtems_stack_checker_report_usage_with_plugin(
427  const rtems_printer* printer
428)
429{
430  uint32_t cpu_max;
431  uint32_t cpu_index;
432
433  rtems_printf(
434     printer,
435     "                             STACK USAGE BY THREAD\n"
436     "ID         NAME                  LOW        HIGH       CURRENT     AVAIL   USED\n"
437  );
438
439  /* iterate over all threads and dump the usage */
440  rtems_task_iterate(
441    Stack_check_Dump_threads_usage,
442    RTEMS_DECONST( rtems_printer *, printer )
443  );
444
445  cpu_max = rtems_get_processor_count();
446
447  for ( cpu_index = 0; cpu_index < cpu_max; ++cpu_index ) {
448    Stack_check_Dump_interrupt_stack_usage(
449      &Stack_check_Interrupt_stack[ cpu_index ],
450      cpu_index,
451      printer
452    );
453  }
454}
455
456void rtems_stack_checker_report_usage( void )
457{
458  rtems_printer printer;
459  rtems_print_printer_printk(&printer);
460  rtems_stack_checker_report_usage_with_plugin( &printer );
461}
Note: See TracBrowser for help on using the repository browser.