cppcheck/cli/cppcheckexecutorsig.cpp

328 lines
11 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* 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>
//#include <features.h> // __USE_DYNAMIC_STACK_SIZE
#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
// 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
#ifdef __USE_DYNAMIC_STACK_SIZE
static constexpr size_t MYSTACKSIZE = 16*1024+32768; // wild guess about a reasonable buffer
#else
static constexpr 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;
return ptr > &a;
}
/* (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.
*/
// cppcheck-suppress constParameterCallback
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 auto* 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();
#ifdef USE_UNIX_BACKTRACE_SUPPORT
bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then.
#endif
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);
#ifdef USE_UNIX_BACKTRACE_SUPPORT
lowMem=true; // educated guess
#endif
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
print_stacktrace(output, true, -1, lowMem);
#endif
if (unexpectedSignal) {
fputs("\nPlease 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&) const, CppCheck& cppcheck)
{
// determine stack vs. heap
char stackVariable;
char *heapVariable=static_cast<char*>(malloc(1));
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;
for (std::map<int, std::string>::const_iterator sig=listofsignals.cbegin(); sig!=listofsignals.cend(); ++sig) {
sigaction(sig->first, &act, nullptr);
}
return (executor.*f)(cppcheck);
}
#endif