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

Last change on this file since b539af8 was b539af8, checked in by Kinsey Moore <kinsey.moore@…>, on 01/26/22 at 22:00:04

cpukit: Prevent error with disabled stack checker

When the stack checker is not enabled, the stack checker reporting
function can still be called. This prevents that call from performing a
null memory access in trying to find the high water mark if the stack
checker was never initialized.

This also introduces a test to ensure this call does not cause a crash.

Closes #4588

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