Files
rtems/cpukit/libmisc/stackchk/check.c
Joel Sherrill 64adc13c86 2011-07-24 Joel Sherrill <joel.sherrill@oarcorp.com>
* ftpd/ftpd.c, libcsupport/src/geteuid.c, libcsupport/src/getgroups.c,
	libcsupport/src/getpgrp.c, libcsupport/src/getpid.c,
	libcsupport/src/getppid.c, libcsupport/src/setpgid.c,
	libcsupport/src/setsid.c, libcsupport/src/utsname.c,
	libmisc/stackchk/check.c, posix/include/rtems/posix/mqueue.h,
	posix/inline/rtems/posix/cond.inl,
	posix/inline/rtems/posix/mqueue.inl,
	posix/inline/rtems/posix/mutex.inl,
	posix/inline/rtems/posix/pthread.inl,
	posix/inline/rtems/posix/semaphore.inl,
	posix/inline/rtems/posix/timer.inl, posix/src/alarm.c,
	posix/src/barrierattrdestroy.c, posix/src/barrierattrgetpshared.c,
	posix/src/barrierattrinit.c, posix/src/barrierattrsetpshared.c,
	posix/src/cleanuppop.c, posix/src/cleanuppush.c,
	posix/src/clockgetcpuclockid.c, posix/src/clockgetenableattr.c,
	posix/src/clockgetres.c, posix/src/clockgettime.c,
	posix/src/clocksetenableattr.c, posix/src/clocksettime.c,
	posix/src/cond.c, posix/src/condattrdestroy.c,
	posix/src/condattrgetpshared.c, posix/src/condattrinit.c,
	posix/src/condattrsetpshared.c, posix/src/condbroadcast.c,
	posix/src/conddefaultattributes.c, posix/src/conddestroy.c,
	posix/src/condinit.c, posix/src/condsignal.c,
	posix/src/condsignalsupp.c, posix/src/condtimedwait.c,
	posix/src/condwait.c, posix/src/condwaitsupp.c,
	posix/src/keygetspecific.c, posix/src/keyrundestructors.c,
	posix/src/keysetspecific.c, posix/src/mprotect.c, posix/src/mqueue.c,
	posix/src/mqueuecreatesupp.c, posix/src/mqueuedeletesupp.c,
	posix/src/mqueuegetattr.c, posix/src/mqueuenotify.c,
	posix/src/mqueuereceive.c, posix/src/mqueuesend.c,
	posix/src/mqueuesendsupp.c, posix/src/mqueuesetattr.c,
	posix/src/mqueuetimedreceive.c, posix/src/mqueuetimedsend.c,
	posix/src/mqueuetranslatereturncode.c, posix/src/mqueueunlink.c,
	posix/src/mutex.c, posix/src/mutexattrdestroy.c,
	posix/src/mutexattrgetprioceiling.c,
	posix/src/mutexattrgetprotocol.c, posix/src/mutexattrgetpshared.c,
	posix/src/mutexattrinit.c, posix/src/mutexattrsetprioceiling.c,
	posix/src/mutexattrsetprotocol.c, posix/src/mutexattrsetpshared.c,
	posix/src/mutexdestroy.c, posix/src/mutexgetprioceiling.c,
	posix/src/mutexinit.c, posix/src/mutexlock.c,
	posix/src/mutexlocksupp.c, posix/src/mutexsetprioceiling.c,
	posix/src/mutextimedlock.c, posix/src/mutextrylock.c,
	posix/src/mutexunlock.c, posix/src/nanosleep.c, posix/src/psignal.c,
	posix/src/psignalchecksignal.c,
	posix/src/psignalclearprocesssignals.c,
	posix/src/psignalclearsignals.c,
	posix/src/psignalsetprocesssignals.c,
	posix/src/psignalunblockthread.c, posix/src/pthreadinitthreads.c,
	posix/src/rwlockattrdestroy.c, posix/src/rwlockattrgetpshared.c,
	posix/src/rwlockattrinit.c, posix/src/rwlockattrsetpshared.c,
	posix/src/semaphore.c, posix/src/semaphoredeletesupp.c,
	posix/src/semaphorenametoid.c, posix/src/semaphorewaitsupp.c,
	posix/src/semclose.c, posix/src/semdestroy.c,
	posix/src/semgetvalue.c, posix/src/seminit.c, posix/src/semopen.c,
	posix/src/sempost.c, posix/src/semtimedwait.c,
	posix/src/semtrywait.c, posix/src/semunlink.c, posix/src/semwait.c,
	posix/src/sysconf.c, posix/src/testcancel.c, posix/src/ualarm.c,
	rtems/src/clockgetuptime.c, rtems/src/clockset.c,
	rtems/src/clocksetnsecshandler.c, rtems/src/clocktick.c,
	rtems/src/clocktodtoseconds.c, rtems/src/clocktodvalidate.c,
	rtems/src/dpmem.c, rtems/src/dpmemcreate.c, rtems/src/dpmemdelete.c,
	rtems/src/dpmemexternal2internal.c, rtems/src/dpmemident.c,
	rtems/src/dpmeminternal2external.c, rtems/src/event.c,
	rtems/src/eventmp.c, rtems/src/eventreceive.c,
	rtems/src/eventseize.c, rtems/src/eventsend.c,
	rtems/src/eventsurrender.c, rtems/src/eventtimeout.c, rtems/src/mp.c,
	rtems/src/msg.c, rtems/src/msgmp.c, rtems/src/msgqallocate.c,
	rtems/src/msgqbroadcast.c, rtems/src/msgqcreate.c,
	rtems/src/msgqdelete.c, rtems/src/msgqflush.c,
	rtems/src/msgqgetnumberpending.c, rtems/src/msgqident.c,
	rtems/src/msgqreceive.c, rtems/src/msgqtranslatereturncode.c,
	rtems/src/msgqurgent.c, rtems/src/part.c, rtems/src/partcreate.c,
	rtems/src/partdelete.c, rtems/src/partgetbuffer.c,
	rtems/src/partident.c, rtems/src/partmp.c,
	rtems/src/partreturnbuffer.c, rtems/src/ratemon.c,
	rtems/src/ratemoncancel.c, rtems/src/ratemoncreate.c,
	rtems/src/ratemondelete.c, rtems/src/ratemongetstatistics.c,
	rtems/src/ratemongetstatus.c, rtems/src/ratemonident.c,
	rtems/src/ratemonperiod.c, rtems/src/ratemonresetstatistics.c,
	rtems/src/ratemontimeout.c, rtems/src/region.c,
	rtems/src/regioncreate.c, rtems/src/regiondelete.c,
	rtems/src/regionextend.c, rtems/src/regiongetfreeinfo.c,
	rtems/src/regiongetinfo.c, rtems/src/regiongetsegment.c,
	rtems/src/regiongetsegmentsize.c, rtems/src/regionident.c,
	rtems/src/regionmp.c, rtems/src/regionprocessqueue.c,
	rtems/src/regionresizesegment.c, rtems/src/regionreturnsegment.c,
	rtems/src/rtemsobjectgetclassicname.c, rtems/src/rtemstimer.c,
	rtems/src/sem.c, rtems/src/semcreate.c, rtems/src/semdelete.c,
	rtems/src/semflush.c, rtems/src/semident.c, rtems/src/semmp.c,
	rtems/src/semobtain.c, rtems/src/semrelease.c,
	rtems/src/semtranslatereturncode.c, rtems/src/signal.c,
	rtems/src/signalcatch.c, rtems/src/signalmp.c,
	rtems/src/signalsend.c, rtems/src/taskcreate.c,
	rtems/src/taskdelete.c, rtems/src/taskgetnote.c,
	rtems/src/taskident.c, rtems/src/taskinitusers.c,
	rtems/src/taskissuspended.c, rtems/src/taskmp.c,
	rtems/src/taskrestart.c, rtems/src/taskresume.c, rtems/src/tasks.c,
	rtems/src/tasksetnote.c, rtems/src/tasksetpriority.c,
	rtems/src/taskstart.c, rtems/src/tasksuspend.c,
	rtems/src/taskwakeafter.c, rtems/src/taskwakewhen.c,
	rtems/src/timercancel.c, rtems/src/timercreate.c,
	rtems/src/timerdelete.c, rtems/src/timerfireafter.c,
	rtems/src/timerfirewhen.c, rtems/src/timergetinfo.c,
	rtems/src/timerident.c, rtems/src/timerreset.c,
	rtems/src/timerserverfireafter.c, rtems/src/timerserverfirewhen.c,
	sapi/src/debug.c, sapi/src/extension.c, sapi/src/posixapi.c,
	sapi/src/rtemsapi.c, score/src/apiext.c, score/src/chain.c,
	score/src/corebarrier.c, score/src/corebarrierrelease.c,
	score/src/corebarrierwait.c, score/src/coremsgbroadcast.c,
	score/src/coremsgclose.c, score/src/coremsgflush.c,
	score/src/coremsgflushsupp.c, score/src/coremsgflushwait.c,
	score/src/coremsginsert.c, score/src/coremsgseize.c,
	score/src/coremsgsubmit.c, score/src/coremutex.c,
	score/src/coremutexflush.c, score/src/coremutexseize.c,
	score/src/corerwlock.c, score/src/corerwlockobtainread.c,
	score/src/corerwlockobtainwrite.c, score/src/corerwlockrelease.c,
	score/src/coresem.c, score/src/coresemflush.c,
	score/src/coresemsurrender.c, score/src/corespinlock.c,
	score/src/corespinlockrelease.c, score/src/corespinlockwait.c,
	score/src/coretod.c, score/src/coretodset.c,
	score/src/coretodtickle.c, score/src/heap.c, score/src/interr.c,
	score/src/mpci.c, score/src/objectallocate.c,
	score/src/objectextendinformation.c, score/src/objectfree.c,
	score/src/objectget.c, score/src/objectgetisr.c,
	score/src/objectgetnext.c, score/src/objectgetnoprotection.c,
	score/src/objectinitializeinformation.c, score/src/objectmp.c,
	score/src/objectnametoid.c, score/src/objectnametoidstring.c,
	score/src/objectshrinkinformation.c, score/src/rbtree.c,
	score/src/threaddelayended.c, score/src/threadhandler.c,
	score/src/threadinitialize.c, score/src/threadloadenv.c,
	score/src/threadmp.c, score/src/threadq.c,
	score/src/threadqdequeue.c, score/src/threadqdequeuefifo.c,
	score/src/threadqdequeuepriority.c, score/src/threadqenqueue.c,
	score/src/threadqenqueuefifo.c, score/src/threadqenqueuepriority.c,
	score/src/threadqextract.c, score/src/threadqextractfifo.c,
	score/src/threadqextractpriority.c,
	score/src/threadqextractwithproxy.c, score/src/threadqfirst.c,
	score/src/threadqfirstfifo.c, score/src/threadqfirstpriority.c,
	score/src/threadqflush.c, score/src/threadqrequeue.c,
	score/src/threadstackallocate.c, score/src/threadstartmultitasking.c,
	score/src/watchdog.c, score/src/watchdogadjust.c,
	score/src/watchdoginsert.c, score/src/watchdogremove.c,
	score/src/watchdogtickle.c: Remove /*PAGE markers which were
	interpreted by a long dead print script.
2011-07-24 23:55:15 +00:00

515 lines
12 KiB
C

/*
* Stack Overflow Check User Extension Set
*
* NOTE: This extension set automatically determines at
* initialization time whether the stack for this
* CPU grows up or down and installs the correct
* extension routines for that direction.
*
* COPYRIGHT (c) 1989-2010.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.com/license/LICENSE.
*
* $Id$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems.h>
#include <inttypes.h>
/*
* The stack dump information may be printed by a "fatal" extension.
* Fatal extensions only get called via rtems_fatal_error_occurred()
* and not when rtems_shutdown_executive() is called.
* When that happens, this #define should be deleted and all the code
* it marks.
*/
#define DONT_USE_FATAL_EXTENSION
#include <string.h>
#include <stdlib.h>
#include <rtems/bspIo.h>
#include <rtems/stackchk.h>
#include "internal.h"
/*
* Variable to indicate when the stack checker has been initialized.
*/
static int Stack_check_Initialized = 0;
/*
* The "magic pattern" used to mark the end of the stack.
*/
Stack_check_Control Stack_check_Pattern;
/*
* Helper function to report if the actual stack pointer is in range.
*
* NOTE: This uses a GCC specific method.
*/
static inline bool Stack_check_Frame_pointer_in_range(
Stack_Control *the_stack
)
{
#if defined(__GNUC__)
void *sp = __builtin_frame_address(0);
if ( sp < the_stack->area ) {
return false;
}
if ( sp > (the_stack->area + the_stack->size) ) {
return false;
}
#else
#error "How do I check stack bounds on a non-GNU compiler?"
#endif
return true;
}
/*
* Where the pattern goes in the stack area is dependent upon
* whether the stack grow to the high or low area of the memory.
*/
#if (CPU_STACK_GROWS_UP == TRUE)
#define Stack_check_Get_pattern( _the_stack ) \
((char *)(_the_stack)->area + \
(_the_stack)->size - sizeof( Stack_check_Control ) )
#define Stack_check_Calculate_used( _low, _size, _high_water ) \
((char *)(_high_water) - (char *)(_low))
#define Stack_check_usable_stack_start(_the_stack) \
((_the_stack)->area)
#else
/*
* We need this magic offset because during a task delete the task stack will
* be freed before we enter the task switch extension which checks the stack.
* The task stack free operation will write the next and previous pointers
* for the free list into this area.
*/
#define Stack_check_Get_pattern( _the_stack ) \
((char *)(_the_stack)->area + sizeof(Heap_Block) - HEAP_BLOCK_HEADER_SIZE)
#define Stack_check_Calculate_used( _low, _size, _high_water) \
( ((char *)(_low) + (_size)) - (char *)(_high_water) )
#define Stack_check_usable_stack_start(_the_stack) \
((char *)(_the_stack)->area + sizeof(Stack_check_Control))
#endif
/*
* Obtain a properly typed pointer to the area to check.
*/
#define Stack_check_Get_pattern_area( _the_stack ) \
(Stack_check_Control *) Stack_check_Get_pattern( _the_stack )
/*
* The assumption is that if the pattern gets overwritten, the task
* is too close. This defines the usable stack memory.
*/
#define Stack_check_usable_stack_size(_the_stack) \
((_the_stack)->size - sizeof(Stack_check_Control))
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
/*
* Did RTEMS allocate the interrupt stack? If so, put it in
* Stack_Control format.
*/
Stack_Control Stack_check_Interrupt_stack;
#endif
/*
* Fill an entire stack area with BYTE_PATTERN. This will be used
* to check for amount of actual stack used.
*/
#define Stack_check_Dope_stack(_stack) \
memset((_stack)->area, BYTE_PATTERN, (_stack)->size)
/*
* Stack_check_Initialize
*/
void Stack_check_Initialize( void )
{
int i;
uint32_t *p;
static uint32_t pattern[ 4 ] = {
0xFEEDF00D, 0x0BAD0D06, /* FEED FOOD to BAD DOG */
0xDEADF00D, 0x600D0D06 /* DEAD FOOD but GOOD DOG */
};
if ( Stack_check_Initialized )
return;
/*
* Dope the pattern and fill areas
*/
p = Stack_check_Pattern.pattern;
for ( i = 0; i < PATTERN_SIZE_WORDS; i++ ) {
p[i] = pattern[ i%4 ];
}
/*
* If appropriate, setup the interrupt stack for high water testing
* also.
*/
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
if (_CPU_Interrupt_stack_low && _CPU_Interrupt_stack_high) {
Stack_check_Interrupt_stack.area = _CPU_Interrupt_stack_low;
Stack_check_Interrupt_stack.size = (char *) _CPU_Interrupt_stack_high -
(char *) _CPU_Interrupt_stack_low;
Stack_check_Dope_stack(&Stack_check_Interrupt_stack);
}
#endif
Stack_check_Initialized = 1;
}
/*
* rtems_stack_checker_create_extension
*/
bool rtems_stack_checker_create_extension(
Thread_Control *running __attribute__((unused)),
Thread_Control *the_thread
)
{
Stack_check_Initialize();
if (the_thread)
Stack_check_Dope_stack(&the_thread->Start.Initial_stack);
return true;
}
/*
* rtems_stack_checker_Begin_extension
*/
void rtems_stack_checker_begin_extension(
Thread_Control *the_thread
)
{
Stack_check_Control *the_pattern;
if ( the_thread->Object.id == 0 ) /* skip system tasks */
return;
the_pattern = Stack_check_Get_pattern_area(&the_thread->Start.Initial_stack);
*the_pattern = Stack_check_Pattern;
}
/*
* Stack_check_report_blown_task
*
* Report a blown stack. Needs to be a separate routine
* so that interrupt handlers can use this too.
*
* NOTE: The system is in a questionable state... we may not get
* the following message out.
*/
void Stack_check_report_blown_task(
Thread_Control *running,
bool pattern_ok
) RTEMS_COMPILER_NO_RETURN_ATTRIBUTE;
void Stack_check_report_blown_task(Thread_Control *running, bool pattern_ok)
{
Stack_Control *stack = &running->Start.Initial_stack;
void *pattern_area = Stack_check_Get_pattern(stack);
char name[32];
printk("BLOWN STACK!!!\n");
printk("task control block: 0x%08" PRIxPTR "\n", running);
printk("task ID: 0x%08lx\n", (unsigned long) running->Object.id);
printk(
"task name: 0x%08" PRIx32 "\n",
running->Object.name.name_u32
);
printk(
"task name string: %s\n",
rtems_object_get_name(running->Object.id, sizeof(name), name)
);
printk(
"task stack area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
(unsigned long) stack->size,
stack->area,
((char *) stack->area + stack->size)
);
if (!pattern_ok) {
printk(
"damaged pattern area (%lu Bytes): 0x%08" PRIxPTR " .. 0x%08" PRIxPTR "\n",
(unsigned long) PATTERN_SIZE_BYTES,
pattern_area,
(pattern_area + PATTERN_SIZE_BYTES)
);
}
#if defined(RTEMS_MULTIPROCESSING)
if (rtems_configuration_get_user_multiprocessing_table()) {
printk(
"node: 0x%08" PRIxPTR "\n",
rtems_configuration_get_user_multiprocessing_table()->node
);
}
#endif
rtems_fatal_error_occurred(0x81);
}
/*
* rtems_stack_checker_switch_extension
*/
void rtems_stack_checker_switch_extension(
Thread_Control *running __attribute__((unused)),
Thread_Control *heir __attribute__((unused))
)
{
Stack_Control *the_stack = &running->Start.Initial_stack;
void *pattern;
bool sp_ok;
bool pattern_ok = true;
pattern = Stack_check_Get_pattern_area(the_stack);
/*
* Check for an out of bounds stack pointer or an overwrite
*/
sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
pattern_ok = (!memcmp( pattern,
(void *) Stack_check_Pattern.pattern, PATTERN_SIZE_BYTES));
if ( !sp_ok || !pattern_ok ) {
Stack_check_report_blown_task( running, pattern_ok );
}
}
/*
* Check if blown
*/
bool rtems_stack_checker_is_blown( void )
{
Stack_Control *the_stack = &_Thread_Executing->Start.Initial_stack;
bool sp_ok;
bool pattern_ok = true;
/*
* Check for an out of bounds stack pointer
*/
sp_ok = Stack_check_Frame_pointer_in_range( the_stack );
/*
* The stack checker must be initialized before the pattern is there
* to check.
*/
if ( Stack_check_Initialized ) {
pattern_ok = (!memcmp(
Stack_check_Get_pattern(the_stack),
(void *) Stack_check_Pattern.pattern,
PATTERN_SIZE_BYTES
));
}
/*
* Let's report as much as we can.
*/
if ( !sp_ok || !pattern_ok ) {
Stack_check_report_blown_task( _Thread_Executing, pattern_ok );
/* DOES NOT RETURN */
}
/*
* The Stack Pointer and the Pattern Area are OK so return false.
*/
return false;
}
/*
* Stack_check_find_high_water_mark
*/
static inline void *Stack_check_find_high_water_mark(
const void *s,
size_t n
)
{
const uint32_t *base, *ebase;
uint32_t length;
base = s;
length = n/4;
#if ( CPU_STACK_GROWS_UP == TRUE )
/*
* start at higher memory and find first word that does not
* match pattern
*/
base += length - 1;
for (ebase = s; base > ebase; base--)
if (*base != U32_PATTERN)
return (void *) base;
#else
/*
* start at lower memory and find first word that does not
* match pattern
*/
base += PATTERN_SIZE_WORDS;
for (ebase = base + length; base < ebase; base++)
if (*base != U32_PATTERN)
return (void *) base;
#endif
return (void *)0;
}
/*
* Stack_check_Dump_threads_usage
*
* Try to print out how much stack was actually used by the task.
*/
static void *print_context;
static rtems_printk_plugin_t print_handler;
void Stack_check_Dump_threads_usage(
Thread_Control *the_thread
)
{
uint32_t size, used;
void *low;
void *high_water_mark;
void *current;
Stack_Control *stack;
char name[5];
/*
* The pointer passed in for the_thread is guaranteed to be non-NULL from
* rtems_iterate_over_all_threads() so no need to check it here.
*/
/*
* Obtain interrupt stack information
*/
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
if (the_thread == (Thread_Control *) -1) {
if (!Stack_check_Interrupt_stack.area)
return;
stack = &Stack_check_Interrupt_stack;
the_thread = 0;
current = 0;
} else
#endif
{
stack = &the_thread->Start.Initial_stack;
current = (void *)_CPU_Context_Get_SP( &the_thread->Registers );
}
low = Stack_check_usable_stack_start(stack);
size = Stack_check_usable_stack_size(stack);
high_water_mark = Stack_check_find_high_water_mark(low, size);
if ( high_water_mark )
used = Stack_check_Calculate_used( low, size, high_water_mark );
else
used = 0;
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
if ( the_thread )
#endif
{
(*print_handler)(
print_context,
"0x%08" PRIx32 " %4s",
the_thread->Object.id,
rtems_object_get_name( the_thread->Object.id, sizeof(name), name )
);
}
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
else {
(*print_handler)( print_context, "0x%08" PRIx32 " INTR", ~0 );
}
#endif
(*print_handler)(
print_context,
" %010p - %010p %010p %8" PRId32 " ",
stack->area,
stack->area + stack->size - 1,
current,
size
);
if (Stack_check_Initialized == 0) {
(*print_handler)( print_context, "Unavailable\n" );
} else {
(*print_handler)( print_context, "%8" PRId32 "\n", used );
}
}
/*
* rtems_stack_checker_fatal_extension
*/
#ifndef DONT_USE_FATAL_EXTENSION
void rtems_stack_checker_fatal_extension(
Internal_errors_Source source,
bool is_internal,
uint32_t status
)
{
if (status == 0)
rtems_stack_checker_report_usage();
}
#endif
/*
* rtems_stack_checker_report_usage
*/
void rtems_stack_checker_report_usage_with_plugin(
void *context,
rtems_printk_plugin_t print
)
{
if ( !print )
return;
print_context = context;
print_handler = print;
(*print)( context, "Stack usage by thread\n");
(*print)( context,
" ID NAME LOW HIGH CURRENT AVAILABLE USED\n"
);
/* iterate over all threads and dump the usage */
rtems_iterate_over_all_threads( Stack_check_Dump_threads_usage );
#if (CPU_ALLOCATE_INTERRUPT_STACK == TRUE)
/* dump interrupt stack info if any */
Stack_check_Dump_threads_usage((Thread_Control *) -1);
#endif
print_context = NULL;
print_handler = NULL;
}
void rtems_stack_checker_report_usage( void )
{
rtems_stack_checker_report_usage_with_plugin( NULL, printk_plugin );
}