2022-08-16 22:03:44 +02:00
/*
* Cppcheck - A tool for static C / C + + code analysis
2023-01-28 10:16:34 +01:00
* Copyright ( C ) 2007 - 2023 Cppcheck team .
2022-08-16 22:03:44 +02:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "cppcheckexecutorsig.h"
# if defined(USE_UNIX_SIGNAL_HANDLING)
# include "cppcheckexecutor.h"
# ifdef USE_UNIX_BACKTRACE_SUPPORT
# include "stacktrace.h"
# endif
# include <csignal>
# include <cstdio>
# include <cstdlib>
# include <cstring>
2023-10-16 12:57:49 +02:00
//#include <features.h> // __USE_DYNAMIC_STACK_SIZE
2022-08-16 22:03:44 +02:00
# include <map>
# include <string>
# include <unistd.h>
# include <utility>
# if defined(__linux__) && defined(REG_ERR)
# include <sys/syscall.h>
# endif
# if defined(__APPLE__)
# define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined
# include <ucontext.h>
# undef _XOPEN_SOURCE
# elif !defined(__OpenBSD__) && !defined(__HAIKU__)
# include <ucontext.h>
# endif
2023-10-17 18:32:07 +02:00
// TODO: __USE_DYNAMIC_STACK_SIZE is dependent on the features.h include and not a built-in compiler define, so it might be problematic to depend on it
2022-08-16 22:03:44 +02:00
# ifdef __USE_DYNAMIC_STACK_SIZE
static const size_t MYSTACKSIZE = 16 * 1024 + 32768 ; // wild guess about a reasonable buffer
# else
static const size_t MYSTACKSIZE = 16 * 1024 + SIGSTKSZ ; // wild guess about a reasonable buffer
# endif
static char mytstack [ MYSTACKSIZE ] = { 0 } ; // alternative stack for signal handler
static bool bStackBelowHeap = false ; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper()
/**
* \ param [ in ] ptr address to be examined .
* \ return true if address is supposed to be on stack ( contrary to heap ) . If ptr is 0 false will be returned .
* If unknown better return false .
*/
static bool IsAddressOnStack ( const void * ptr )
{
if ( nullptr = = ptr )
return false ;
char a ;
if ( bStackBelowHeap )
return ptr < & a ;
2023-06-20 18:43:21 +02:00
return ptr > & a ;
2022-08-16 22:03:44 +02:00
}
/* (declare this list here, so it may be used in signal handlers in addition to main())
* A list of signals available in ISO C
* Check out http : //pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
* For now we only want to detect abnormal behaviour for a few selected signals :
*/
# define DECLARE_SIGNAL(x) std::make_pair(x, #x)
using Signalmap_t = std : : map < int , std : : string > ;
static const Signalmap_t listofsignals = {
DECLARE_SIGNAL ( SIGABRT ) ,
DECLARE_SIGNAL ( SIGBUS ) ,
DECLARE_SIGNAL ( SIGFPE ) ,
DECLARE_SIGNAL ( SIGILL ) ,
DECLARE_SIGNAL ( SIGINT ) ,
DECLARE_SIGNAL ( SIGQUIT ) ,
DECLARE_SIGNAL ( SIGSEGV ) ,
DECLARE_SIGNAL ( SIGSYS ) ,
// don't care: SIGTERM
DECLARE_SIGNAL ( SIGUSR1 ) ,
//DECLARE_SIGNAL(SIGUSR2) no usage currently
} ;
# undef DECLARE_SIGNAL
/*
* Entry pointer for signal handlers
* It uses functions which are not safe to be called from a signal handler ,
* ( http : //pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist)
* but when ending up here something went terribly wrong anyway .
* And all which is left is just printing some information and terminate .
*/
2023-08-14 10:29:04 +02:00
// cppcheck-suppress constParameterCallback
2022-08-16 22:03:44 +02:00
static void CppcheckSignalHandler ( int signo , siginfo_t * info , void * context )
{
int type = - 1 ;
pid_t killid ;
// TODO: separate these two defines
# if defined(__linux__) && defined(REG_ERR)
const ucontext_t * const uc = reinterpret_cast < const ucontext_t * > ( context ) ;
killid = ( pid_t ) syscall ( SYS_gettid ) ;
if ( uc ) {
type = ( int ) uc - > uc_mcontext . gregs [ REG_ERR ] & 2 ;
}
# else
( void ) context ;
killid = getpid ( ) ;
# endif
const Signalmap_t : : const_iterator it = listofsignals . find ( signo ) ;
const char * const signame = ( it = = listofsignals . end ( ) ) ? " unknown " : it - > second . c_str ( ) ;
2023-08-14 10:29:04 +02:00
# ifdef USE_UNIX_BACKTRACE_SUPPORT
2022-08-16 22:03:44 +02:00
bool lowMem = false ; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then.
2023-08-14 10:29:04 +02:00
# endif
2022-08-16 22:03:44 +02:00
bool unexpectedSignal = true ; // unexpected indicates program failure
bool terminate = true ; // exit process/thread
const bool isAddressOnStack = IsAddressOnStack ( info - > si_addr ) ;
FILE * output = CppCheckExecutor : : getExceptionOutput ( ) ;
switch ( signo ) {
case SIGABRT :
fputs ( " Internal error: cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
fputs (
# ifdef NDEBUG
" - out of memory? \n " ,
# else
" - out of memory or assertion? \n " ,
# endif
output ) ;
2023-08-14 10:29:04 +02:00
# ifdef USE_UNIX_BACKTRACE_SUPPORT
2022-08-16 22:03:44 +02:00
lowMem = true ; // educated guess
2023-08-14 10:29:04 +02:00
# endif
2022-08-16 22:03:44 +02:00
break ;
case SIGBUS :
fputs ( " Internal error: cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
switch ( info - > si_code ) {
case BUS_ADRALN : // invalid address alignment
fputs ( " - BUS_ADRALN " , output ) ;
break ;
case BUS_ADRERR : // nonexistent physical address
fputs ( " - BUS_ADRERR " , output ) ;
break ;
case BUS_OBJERR : // object-specific hardware error
fputs ( " - BUS_OBJERR " , output ) ;
break ;
# ifdef BUS_MCEERR_AR
case BUS_MCEERR_AR : // Hardware memory error consumed on a machine check;
fputs ( " - BUS_MCEERR_AR " , output ) ;
break ;
# endif
# ifdef BUS_MCEERR_AO
case BUS_MCEERR_AO : // Hardware memory error detected in process but not consumed
fputs ( " - BUS_MCEERR_AO " , output ) ;
break ;
# endif
default :
break ;
}
fprintf ( output , " (at 0x%lx). \n " ,
( unsigned long ) info - > si_addr ) ;
break ;
case SIGFPE :
fputs ( " Internal error: cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
switch ( info - > si_code ) {
case FPE_INTDIV : // integer divide by zero
fputs ( " - FPE_INTDIV " , output ) ;
break ;
case FPE_INTOVF : // integer overflow
fputs ( " - FPE_INTOVF " , output ) ;
break ;
case FPE_FLTDIV : // floating-point divide by zero
fputs ( " - FPE_FLTDIV " , output ) ;
break ;
case FPE_FLTOVF : // floating-point overflow
fputs ( " - FPE_FLTOVF " , output ) ;
break ;
case FPE_FLTUND : // floating-point underflow
fputs ( " - FPE_FLTUND " , output ) ;
break ;
case FPE_FLTRES : // floating-point inexact result
fputs ( " - FPE_FLTRES " , output ) ;
break ;
case FPE_FLTINV : // floating-point invalid operation
fputs ( " - FPE_FLTINV " , output ) ;
break ;
case FPE_FLTSUB : // subscript out of range
fputs ( " - FPE_FLTSUB " , output ) ;
break ;
default :
break ;
}
fprintf ( output , " (at 0x%lx). \n " ,
( unsigned long ) info - > si_addr ) ;
break ;
case SIGILL :
fputs ( " Internal error: cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
switch ( info - > si_code ) {
case ILL_ILLOPC : // illegal opcode
fputs ( " - ILL_ILLOPC " , output ) ;
break ;
case ILL_ILLOPN : // illegal operand
fputs ( " - ILL_ILLOPN " , output ) ;
break ;
case ILL_ILLADR : // illegal addressing mode
fputs ( " - ILL_ILLADR " , output ) ;
break ;
case ILL_ILLTRP : // illegal trap
fputs ( " - ILL_ILLTRP " , output ) ;
break ;
case ILL_PRVOPC : // privileged opcode
fputs ( " - ILL_PRVOPC " , output ) ;
break ;
case ILL_PRVREG : // privileged register
fputs ( " - ILL_PRVREG " , output ) ;
break ;
case ILL_COPROC : // coprocessor error
fputs ( " - ILL_COPROC " , output ) ;
break ;
case ILL_BADSTK : // internal stack error
fputs ( " - ILL_BADSTK " , output ) ;
break ;
default :
break ;
}
fprintf ( output , " (at 0x%lx).%s \n " ,
( unsigned long ) info - > si_addr ,
( isAddressOnStack ) ? " Stackoverflow? " : " " ) ;
break ;
case SIGINT :
unexpectedSignal = false ; // legal usage: interrupt application via CTRL-C
fputs ( " cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
fputs ( " . \n " , output ) ;
break ;
case SIGSEGV :
fputs ( " Internal error: cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
switch ( info - > si_code ) {
case SEGV_MAPERR : // address not mapped to object
fputs ( " - SEGV_MAPERR " , output ) ;
break ;
case SEGV_ACCERR : // invalid permissions for mapped object
fputs ( " - SEGV_ACCERR " , output ) ;
break ;
default :
break ;
}
fprintf ( output , " (%sat 0x%lx).%s \n " ,
// cppcheck-suppress knownConditionTrueFalse ; FP
( type = = - 1 ) ? " " :
( type = = 0 ) ? " reading " : " writing " ,
( unsigned long ) info - > si_addr ,
( isAddressOnStack ) ? " Stackoverflow? " : " "
) ;
break ;
case SIGUSR1 :
fputs ( " cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
fputs ( " . \n " , output ) ;
terminate = false ;
break ;
default :
fputs ( " Internal error: cppcheck received signal " , output ) ;
fputs ( signame , output ) ;
fputs ( " . \n " , output ) ;
break ;
}
# ifdef USE_UNIX_BACKTRACE_SUPPORT
2023-04-28 16:02:41 +02:00
print_stacktrace ( output , true , - 1 , lowMem ) ;
2022-08-16 22:03:44 +02:00
# endif
if ( unexpectedSignal ) {
fputs ( " \n Please report this to the cppcheck developers! \n " , output ) ;
}
fflush ( output ) ;
if ( terminate ) {
// now let things proceed, shutdown and hopefully dump core for post-mortem analysis
struct sigaction act ;
memset ( & act , 0 , sizeof ( act ) ) ;
act . sa_handler = SIG_DFL ;
sigaction ( signo , & act , nullptr ) ;
kill ( killid , signo ) ;
}
}
int check_wrapper_sig ( CppCheckExecutor & executor , int ( CppCheckExecutor : : * f ) ( CppCheck & ) , CppCheck & cppcheck )
{
// determine stack vs. heap
char stackVariable ;
2023-08-14 10:29:04 +02:00
char * heapVariable = static_cast < char * > ( malloc ( 1 ) ) ;
2022-08-16 22:03:44 +02:00
bStackBelowHeap = & stackVariable < heapVariable ;
free ( heapVariable ) ;
// set up alternative stack for signal handler
stack_t segv_stack ;
segv_stack . ss_sp = mytstack ;
segv_stack . ss_flags = 0 ;
segv_stack . ss_size = MYSTACKSIZE ;
sigaltstack ( & segv_stack , nullptr ) ;
// install signal handler
struct sigaction act ;
memset ( & act , 0 , sizeof ( act ) ) ;
act . sa_flags = SA_SIGINFO | SA_ONSTACK ;
act . sa_sigaction = CppcheckSignalHandler ;
2022-12-20 20:32:16 +01:00
for ( std : : map < int , std : : string > : : const_iterator sig = listofsignals . cbegin ( ) ; sig ! = listofsignals . cend ( ) ; + + sig ) {
2022-08-16 22:03:44 +02:00
sigaction ( sig - > first , & act , nullptr ) ;
}
2023-10-16 14:09:03 +02:00
return ( executor . * f ) ( cppcheck ) ;
2022-08-16 22:03:44 +02:00
}
# endif