diff --git a/Makefile b/Makefile index de56620ae..6cae34dbb 100644 --- a/Makefile +++ b/Makefile @@ -209,10 +209,13 @@ EXTOBJ = externals/simplecpp/simplecpp.o \ CLIOBJ = cli/cmdlineparser.o \ cli/cppcheckexecutor.o \ + cli/cppcheckexecutorseh.o \ + cli/cppcheckexecutorsig.o \ cli/executor.o \ cli/filelister.o \ cli/main.o \ cli/processexecutor.o \ + cli/stacktrace.o \ cli/threadexecutor.o TESTOBJ = test/options.o \ @@ -291,7 +294,7 @@ cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ) all: cppcheck testrunner -testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o +testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) test: all @@ -574,9 +577,15 @@ $(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/ca cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp -cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h +cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp +cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutorseh.o cli/cppcheckexecutorseh.cpp + +cli/cppcheckexecutorsig.o: cli/cppcheckexecutorsig.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorsig.h cli/stacktrace.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutorsig.o cli/cppcheckexecutorsig.cpp + cli/executor.o: cli/executor.cpp cli/executor.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/executor.o cli/executor.cpp @@ -589,6 +598,9 @@ cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/color.h lib/config.h lib/err cli/processexecutor.o: cli/processexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/processexecutor.o cli/processexecutor.cpp +cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/stacktrace.o cli/stacktrace.cpp + cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp diff --git a/cli/cli.vcxproj b/cli/cli.vcxproj index 9a2db8671..eb04e5dbf 100644 --- a/cli/cli.vcxproj +++ b/cli/cli.vcxproj @@ -407,10 +407,13 @@ + + + @@ -420,11 +423,14 @@ + + + diff --git a/cli/cli.vcxproj.filters b/cli/cli.vcxproj.filters index f95e472bb..a378aeb51 100644 --- a/cli/cli.vcxproj.filters +++ b/cli/cli.vcxproj.filters @@ -26,9 +26,18 @@ Header Files + + Header Files + + + Header Files + Header Files + + Header Files + @@ -40,12 +49,21 @@ Source Files + + Source Files + + + Source Files + Source Files Source Files + + Source Files + diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 446642a68..bc8e5ec77 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -341,6 +341,7 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[]) else if (std::strcmp(argv[i], "--exception-handling") == 0) mSettings->exceptionHandling = true; + // TODO: only applied with Signal handling i.e. Linux else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) { mSettings->exceptionHandling = true; const std::string exceptionOutfilename = &(argv[i][21]); diff --git a/cli/cppcheckexecutor.cpp b/cli/cppcheckexecutor.cpp index b622adeba..5e78cc7dc 100644 --- a/cli/cppcheckexecutor.cpp +++ b/cli/cppcheckexecutor.cpp @@ -41,56 +41,26 @@ #include "processexecutor.h" #endif -#include #include -#include #include #include // EXIT_SUCCESS and EXIT_FAILURE -#include #include #include #include #include -#include #include #include -#if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__) -#define USE_UNIX_SIGNAL_HANDLING -#include -#if defined(__APPLE__) -# define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined -# include - -# undef _XOPEN_SOURCE -#elif !defined(__OpenBSD__) && !defined(__HAIKU__) -# include -#endif -#ifdef __linux__ -#include -#endif +#ifdef USE_UNIX_SIGNAL_HANDLING +#include "cppcheckexecutorsig.h" #endif -#if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(USE_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__) -#define USE_UNIX_BACKTRACE_SUPPORT -#include -#include +#ifdef USE_WINDOWS_SEH +#include "cppcheckexecutorseh.h" #endif -#if defined(_WIN32) -#if defined(_MSC_VER) -#define USE_WINDOWS_SEH -#endif -#if defined (__MINGW32__) -# include -# include -# include -#else -# include -# include -# include -#endif -#include +#ifdef _WIN32 +#include #endif @@ -264,605 +234,12 @@ void CppCheckExecutor::setSettings(const Settings &settings) mSettings = &settings; } -/** - * Simple helper function: - * \return size of array - * */ -template -std::size_t getArrayLength(const T (&)[size]) -{ - return size; -} - - -#if defined(USE_UNIX_SIGNAL_HANDLING) -/* - * Try to print the callstack. - * That is very sensitive to the operating system, hardware, compiler and runtime. - * The code is not meant for production environment! - * One reason is named first: it's using functions not whitelisted for usage in a signal handler function. - */ -static void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem) -{ -#if defined(USE_UNIX_BACKTRACE_SUPPORT) -// 32 vs. 64bit -#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8) - const int fd = fileno(output); - void *callstackArray[32]= {nullptr}; // the less resources the better... - const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray)); - const int offset=2; // some entries on top are within our own exception handling code or libc - if (maxdepth<0) - maxdepth=currentdepth-offset; - else - maxdepth = std::min(maxdepth, currentdepth); - if (lowMem) { - fputs("Callstack (symbols only):\n", output); - backtrace_symbols_fd(callstackArray+offset, maxdepth, fd); - } else { - char **symbolStringList = backtrace_symbols(callstackArray, currentdepth); - if (symbolStringList) { - fputs("Callstack:\n", output); - char demangle_buffer[2048]= {0}; - for (int i = offset; i < maxdepth; ++i) { - const char * const symbolString = symbolStringList[i]; - char * realnameString = nullptr; - const char * const firstBracketName = strchr(symbolString, '('); - const char * const firstBracketAddress = strchr(symbolString, '['); - const char * const secondBracketAddress = strchr(firstBracketAddress, ']'); - const char * const beginAddress = firstBracketAddress+3; - const int addressLen = int(secondBracketAddress-beginAddress); - const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen); - if (demangling && firstBracketName) { - const char * const plus = strchr(firstBracketName, '+'); - if (plus && (plus>(firstBracketName+1))) { - char input_buffer[1024]= {0}; - strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1); - size_t length = getArrayLength(demangle_buffer); - int status=0; - // We're violating the specification - passing stack address instead of malloc'ed heap. - // Benefit is that no further heap is required, while there is sufficient stack... - realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success - } - } - const int ordinal=i-offset; - fprintf(output, "#%-2d 0x", - ordinal); - if (padLen>0) - fprintf(output, "%0*d", - padLen, 0); - if (realnameString) { - fprintf(output, "%.*s in %s\n", - (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, - realnameString); - } else { - fprintf(output, "%.*s in %.*s\n", - (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, - (int)(firstBracketAddress-symbolString), symbolString); - } - } - free(symbolStringList); - } else { - fputs("Callstack could not be obtained\n", output); - } - } -#undef ADDRESSDISPLAYLENGTH -#else - (void)output; - (void)demangling; - (void)maxdepth; - (void)lowMem; -#endif -} - -#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; - else - 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; -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. - */ -static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context) -{ - int type = -1; - pid_t killid; -#if defined(__linux__) && defined(REG_ERR) - const ucontext_t* const uc = reinterpret_cast(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(); - bool printCallstack=true; // try to print a callstack? - bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then. - 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); - lowMem=true; // educated guess - 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); - printCallstack=true; - 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; - } - if (printCallstack) { - print_stacktrace(output, true, -1, lowMem); - } - 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); - } -} -#endif - -#ifdef USE_WINDOWS_SEH -namespace { - const ULONG maxnamelength = 512; - struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 { - TCHAR nameExt[maxnamelength]; // actually no need to worry about character encoding here - }; - typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64); - fpStackWalk64 pStackWalk64; - typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); - fpSymGetModuleBase64 pSymGetModuleBase64; - typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); - fpSymGetSymFromAddr64 pSymGetSymFromAddr64; - typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64); - fpSymGetLineFromAddr64 pSymGetLineFromAddr64; - typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD); - fpUnDecorateSymbolName pUnDecorateSymbolName; - typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); - fpSymFunctionTableAccess64 pSymFunctionTableAccess64; - typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL); - fpSymInitialize pSymInitialize; - - HMODULE hLibDbgHelp; -// avoid explicit dependency on Dbghelp.dll - bool loadDbgHelp() - { - hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll"); - if (!hLibDbgHelp) - return false; - pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64"); - pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64"); - pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64"); - pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64"); - pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64"); - pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize"); - pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName"); - return true; - } - - - void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex) - { - if (!loadDbgHelp()) - return; - const HANDLE hProcess = GetCurrentProcess(); - const HANDLE hThread = GetCurrentThread(); - pSymInitialize( - hProcess, - nullptr, - TRUE - ); - CONTEXT context = *(ex->ContextRecord); - STACKFRAME64 stack= {0}; -#ifdef _M_IX86 - stack.AddrPC.Offset = context.Eip; - stack.AddrPC.Mode = AddrModeFlat; - stack.AddrStack.Offset = context.Esp; - stack.AddrStack.Mode = AddrModeFlat; - stack.AddrFrame.Offset = context.Ebp; - stack.AddrFrame.Mode = AddrModeFlat; -#else - stack.AddrPC.Offset = context.Rip; - stack.AddrPC.Mode = AddrModeFlat; - stack.AddrStack.Offset = context.Rsp; - stack.AddrStack.Mode = AddrModeFlat; - stack.AddrFrame.Offset = context.Rsp; - stack.AddrFrame.Mode = AddrModeFlat; -#endif - IMAGEHLP_SYMBOL64_EXT symbol; - symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); - symbol.MaxNameLength = maxnamelength; - DWORD64 displacement = 0; - int beyond_main=-1; // emergency exit, see below - for (ULONG frame = 0; ; frame++) { - BOOL result = pStackWalk64 - ( -#ifdef _M_IX86 - IMAGE_FILE_MACHINE_I386, -#else - IMAGE_FILE_MACHINE_AMD64, -#endif - hProcess, - hThread, - &stack, - &context, - nullptr, - pSymFunctionTableAccess64, - pSymGetModuleBase64, - nullptr - ); - if (!result) // official end... - break; - pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol); - TCHAR undname[maxnamelength]= {0}; - pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE); - if (beyond_main>=0) - ++beyond_main; - if (_tcscmp(undname, _T("main"))==0) - beyond_main=0; - fprintf(outputFile, - "%lu. 0x%08I64X in ", - frame, (ULONG64)stack.AddrPC.Offset); - fputs((const char *)undname, outputFile); - fputc('\n', outputFile); - if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end... - break; - } - - FreeLibrary(hLibDbgHelp); - hLibDbgHelp=nullptr; - } - - void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description) - { - fputs(description, outputFile); - fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress); - // Using %p for ULONG_PTR later on, so it must have size identical to size of pointer - // This is not the universally portable solution but good enough for Win32/64 - C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1])); - switch (ex->ExceptionRecord->ExceptionInformation[0]) { - case 0: - fprintf(outputFile, "reading from 0x%p", - reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); - break; - case 1: - fprintf(outputFile, "writing to 0x%p", - reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); - break; - case 8: - fprintf(outputFile, "data execution prevention at 0x%p", - reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); - break; - default: - break; - } - } - - /* - * Any evaluation of the exception needs to be done here! - */ - int filterException(int code, PEXCEPTION_POINTERS ex) - { - FILE *outputFile = stdout; - fputs("Internal error: ", outputFile); - switch (ex->ExceptionRecord->ExceptionCode) { - case EXCEPTION_ACCESS_VIOLATION: - writeMemoryErrorDetails(outputFile, ex, "Access violation"); - break; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - fputs("Out of array bounds", outputFile); - break; - case EXCEPTION_BREAKPOINT: - fputs("Breakpoint", outputFile); - break; - case EXCEPTION_DATATYPE_MISALIGNMENT: - fputs("Misaligned data", outputFile); - break; - case EXCEPTION_FLT_DENORMAL_OPERAND: - fputs("Denormalized floating-point value", outputFile); - break; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: - fputs("Floating-point divide-by-zero", outputFile); - break; - case EXCEPTION_FLT_INEXACT_RESULT: - fputs("Inexact floating-point value", outputFile); - break; - case EXCEPTION_FLT_INVALID_OPERATION: - fputs("Invalid floating-point operation", outputFile); - break; - case EXCEPTION_FLT_OVERFLOW: - fputs("Floating-point overflow", outputFile); - break; - case EXCEPTION_FLT_STACK_CHECK: - fputs("Floating-point stack overflow", outputFile); - break; - case EXCEPTION_FLT_UNDERFLOW: - fputs("Floating-point underflow", outputFile); - break; - case EXCEPTION_GUARD_PAGE: - fputs("Page-guard access", outputFile); - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - fputs("Illegal instruction", outputFile); - break; - case EXCEPTION_IN_PAGE_ERROR: - writeMemoryErrorDetails(outputFile, ex, "Invalid page access"); - break; - case EXCEPTION_INT_DIVIDE_BY_ZERO: - fputs("Integer divide-by-zero", outputFile); - break; - case EXCEPTION_INT_OVERFLOW: - fputs("Integer overflow", outputFile); - break; - case EXCEPTION_INVALID_DISPOSITION: - fputs("Invalid exception dispatcher", outputFile); - break; - case EXCEPTION_INVALID_HANDLE: - fputs("Invalid handle", outputFile); - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - fputs("Non-continuable exception", outputFile); - break; - case EXCEPTION_PRIV_INSTRUCTION: - fputs("Invalid instruction", outputFile); - break; - case EXCEPTION_SINGLE_STEP: - fputs("Single instruction step", outputFile); - break; - case EXCEPTION_STACK_OVERFLOW: - fputs("Stack overflow", outputFile); - break; - default: - fprintf(outputFile, "Unknown exception (%d)\n", - code); - break; - } - fputc('\n', outputFile); - printCallstack(outputFile, ex); - fflush(outputFile); - return EXCEPTION_EXECUTE_HANDLER; - } -} -#endif - -/** - * Signal/SEH handling - * Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed! - * TODO Check for multi-threading issues! - * - */ int CppCheckExecutor::check_wrapper(CppCheck& cppcheck) { #ifdef USE_WINDOWS_SEH - FILE *outputFile = stdout; - __try { - return check_internal(cppcheck); - } __except (filterException(GetExceptionCode(), GetExceptionInformation())) { - // reporting to stdout may not be helpful within a GUI application... - fputs("Please report this to the cppcheck developers!\n", outputFile); - return -1; - } + return check_wrapper_seh(*this, &CppCheckExecutor::check_internal, cppcheck); #elif defined(USE_UNIX_SIGNAL_HANDLING) - // determine stack vs. heap - char stackVariable; - char *heapVariable=(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::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) { - sigaction(sig->first, &act, nullptr); - } - return check_internal(cppcheck); + return check_wrapper_sig(*this, &CppCheckExecutor::check_internal, cppcheck); #else return check_internal(cppcheck); #endif diff --git a/cli/cppcheckexecutorseh.cpp b/cli/cppcheckexecutorseh.cpp new file mode 100644 index 000000000..b0d3a1425 --- /dev/null +++ b/cli/cppcheckexecutorseh.cpp @@ -0,0 +1,269 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 . + */ + +#include "cppcheckexecutorseh.h" + +#ifdef USE_WINDOWS_SEH + +#include "cppcheckexecutor.h" +#include "utils.h" + +#include +#include +#include + +namespace { + const ULONG maxnamelength = 512; + struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 { + TCHAR nameExt[maxnamelength]; // actually no need to worry about character encoding here + }; + typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64); + fpStackWalk64 pStackWalk64; + typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64); + fpSymGetModuleBase64 pSymGetModuleBase64; + typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); + fpSymGetSymFromAddr64 pSymGetSymFromAddr64; + typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64); + fpSymGetLineFromAddr64 pSymGetLineFromAddr64; + typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD); + fpUnDecorateSymbolName pUnDecorateSymbolName; + typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64); + fpSymFunctionTableAccess64 pSymFunctionTableAccess64; + typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL); + fpSymInitialize pSymInitialize; + + HMODULE hLibDbgHelp; +// avoid explicit dependency on Dbghelp.dll + bool loadDbgHelp() + { + hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll"); + if (!hLibDbgHelp) + return false; + pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64"); + pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64"); + pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64"); + pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64"); + pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64"); + pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize"); + pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName"); + return true; + } + + + void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex) + { + if (!loadDbgHelp()) + return; + const HANDLE hProcess = GetCurrentProcess(); + const HANDLE hThread = GetCurrentThread(); + pSymInitialize( + hProcess, + nullptr, + TRUE + ); + CONTEXT context = *(ex->ContextRecord); + STACKFRAME64 stack= {0}; +#ifdef _M_IX86 + stack.AddrPC.Offset = context.Eip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrStack.Offset = context.Esp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrFrame.Offset = context.Ebp; + stack.AddrFrame.Mode = AddrModeFlat; +#else + stack.AddrPC.Offset = context.Rip; + stack.AddrPC.Mode = AddrModeFlat; + stack.AddrStack.Offset = context.Rsp; + stack.AddrStack.Mode = AddrModeFlat; + stack.AddrFrame.Offset = context.Rsp; + stack.AddrFrame.Mode = AddrModeFlat; +#endif + IMAGEHLP_SYMBOL64_EXT symbol; + symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + symbol.MaxNameLength = maxnamelength; + DWORD64 displacement = 0; + int beyond_main=-1; // emergency exit, see below + for (ULONG frame = 0; ; frame++) { + BOOL result = pStackWalk64 + ( +#ifdef _M_IX86 + IMAGE_FILE_MACHINE_I386, +#else + IMAGE_FILE_MACHINE_AMD64, +#endif + hProcess, + hThread, + &stack, + &context, + nullptr, + pSymFunctionTableAccess64, + pSymGetModuleBase64, + nullptr + ); + if (!result) // official end... + break; + pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol); + TCHAR undname[maxnamelength]= {0}; + pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE); + if (beyond_main>=0) + ++beyond_main; + if (_tcscmp(undname, _T("main"))==0) + beyond_main=0; + fprintf(outputFile, + "%lu. 0x%08I64X in ", + frame, (ULONG64)stack.AddrPC.Offset); + fputs((const char *)undname, outputFile); + fputc('\n', outputFile); + if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end... + break; + } + + FreeLibrary(hLibDbgHelp); + hLibDbgHelp=nullptr; + } + + void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description) + { + fputs(description, outputFile); + fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress); + // Using %p for ULONG_PTR later on, so it must have size identical to size of pointer + // This is not the universally portable solution but good enough for Win32/64 + C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1])); + switch (ex->ExceptionRecord->ExceptionInformation[0]) { + case 0: + fprintf(outputFile, "reading from 0x%p", + reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); + break; + case 1: + fprintf(outputFile, "writing to 0x%p", + reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); + break; + case 8: + fprintf(outputFile, "data execution prevention at 0x%p", + reinterpret_cast(ex->ExceptionRecord->ExceptionInformation[1])); + break; + default: + break; + } + } + + /* + * Any evaluation of the exception needs to be done here! + */ + int filterException(int code, PEXCEPTION_POINTERS ex) + { + FILE *outputFile = stdout; + fputs("Internal error: ", outputFile); + switch (ex->ExceptionRecord->ExceptionCode) { + case EXCEPTION_ACCESS_VIOLATION: + writeMemoryErrorDetails(outputFile, ex, "Access violation"); + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + fputs("Out of array bounds", outputFile); + break; + case EXCEPTION_BREAKPOINT: + fputs("Breakpoint", outputFile); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + fputs("Misaligned data", outputFile); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + fputs("Denormalized floating-point value", outputFile); + break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + fputs("Floating-point divide-by-zero", outputFile); + break; + case EXCEPTION_FLT_INEXACT_RESULT: + fputs("Inexact floating-point value", outputFile); + break; + case EXCEPTION_FLT_INVALID_OPERATION: + fputs("Invalid floating-point operation", outputFile); + break; + case EXCEPTION_FLT_OVERFLOW: + fputs("Floating-point overflow", outputFile); + break; + case EXCEPTION_FLT_STACK_CHECK: + fputs("Floating-point stack overflow", outputFile); + break; + case EXCEPTION_FLT_UNDERFLOW: + fputs("Floating-point underflow", outputFile); + break; + case EXCEPTION_GUARD_PAGE: + fputs("Page-guard access", outputFile); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + fputs("Illegal instruction", outputFile); + break; + case EXCEPTION_IN_PAGE_ERROR: + writeMemoryErrorDetails(outputFile, ex, "Invalid page access"); + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + fputs("Integer divide-by-zero", outputFile); + break; + case EXCEPTION_INT_OVERFLOW: + fputs("Integer overflow", outputFile); + break; + case EXCEPTION_INVALID_DISPOSITION: + fputs("Invalid exception dispatcher", outputFile); + break; + case EXCEPTION_INVALID_HANDLE: + fputs("Invalid handle", outputFile); + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + fputs("Non-continuable exception", outputFile); + break; + case EXCEPTION_PRIV_INSTRUCTION: + fputs("Invalid instruction", outputFile); + break; + case EXCEPTION_SINGLE_STEP: + fputs("Single instruction step", outputFile); + break; + case EXCEPTION_STACK_OVERFLOW: + fputs("Stack overflow", outputFile); + break; + default: + fprintf(outputFile, "Unknown exception (%d)\n", + code); + break; + } + fputc('\n', outputFile); + printCallstack(outputFile, ex); + fflush(outputFile); + return EXCEPTION_EXECUTE_HANDLER; + } +} + +/** + * Signal/SEH handling + * Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed! + * TODO Check for multi-threading issues! + * + */ +int check_wrapper_seh(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck) +{ + FILE *outputFile = stdout; + __try { + return (&executor->*f)(cppcheck); + } __except (filterException(GetExceptionCode(), GetExceptionInformation())) { + // reporting to stdout may not be helpful within a GUI application... + fputs("Please report this to the cppcheck developers!\n", outputFile); + return -1; + } +} + +#endif diff --git a/cli/cppcheckexecutorseh.h b/cli/cppcheckexecutorseh.h new file mode 100644 index 000000000..487cc9221 --- /dev/null +++ b/cli/cppcheckexecutorseh.h @@ -0,0 +1,33 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 . + */ + +#ifndef CPPCHECKEXECUTORSEH_H +#define CPPCHECKEXECUTORSEH_H + +#include "config.h" + +#ifdef USE_WINDOWS_SEH + +class CppCheckExecutor; +class CppCheck; + +int check_wrapper_seh(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck); + +#endif + +#endif // CPPCHECKEXECUTORSEH_H diff --git a/cli/cppcheckexecutorsig.cpp b/cli/cppcheckexecutorsig.cpp new file mode 100644 index 000000000..5f8943da2 --- /dev/null +++ b/cli/cppcheckexecutorsig.cpp @@ -0,0 +1,326 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 . + */ + +#include "cppcheckexecutorsig.h" + +#if defined(USE_UNIX_SIGNAL_HANDLING) + +#include "cppcheckexecutor.h" + +#ifdef USE_UNIX_BACKTRACE_SUPPORT +#include "stacktrace.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) && defined(REG_ERR) +#include +#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 + +# undef _XOPEN_SOURCE +#elif !defined(__OpenBSD__) && !defined(__HAIKU__) +# include +#endif + + +#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; + else + 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; +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. + */ +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(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(); + bool printCallstack=true; // try to print a callstack? + bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then. + 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); + lowMem=true; // educated guess + 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); + printCallstack=true; + 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; + } + if (printCallstack) { +#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&), CppCheck& cppcheck) +{ + // determine stack vs. heap + char stackVariable; + char *heapVariable=(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::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) { + sigaction(sig->first, &act, nullptr); + } + return (&executor->*f)(cppcheck); +} + +#endif diff --git a/cli/cppcheckexecutorsig.h b/cli/cppcheckexecutorsig.h new file mode 100644 index 000000000..4279378e4 --- /dev/null +++ b/cli/cppcheckexecutorsig.h @@ -0,0 +1,33 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 . + */ + +#ifndef CPPCHECKEXECUTORSIG_H +#define CPPCHECKEXECUTORSIG_H + +#include "config.h" + +#if defined(USE_UNIX_SIGNAL_HANDLING) + +class CppCheckExecutor; +class CppCheck; + +int check_wrapper_sig(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck); + +#endif // CPPCHECKEXECUTORSIG_H + +#endif // CPPCHECKEXECUTORSIG_H diff --git a/cli/stacktrace.cpp b/cli/stacktrace.cpp new file mode 100644 index 000000000..bd766c717 --- /dev/null +++ b/cli/stacktrace.cpp @@ -0,0 +1,96 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 . + */ + +#include "stacktrace.h" + +#ifdef USE_UNIX_BACKTRACE_SUPPORT + +#include "utils.h" + +#include +#include +#include +#include +#include + +void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem) +{ + // 32 vs. 64bit +#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8) + const int fd = fileno(output); + void *callstackArray[32]= {nullptr}; // the less resources the better... + const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray)); + const int offset=2; // some entries on top are within our own exception handling code or libc + if (maxdepth<0) + maxdepth=currentdepth-offset; + else + maxdepth = std::min(maxdepth, currentdepth); + if (lowMem) { + fputs("Callstack (symbols only):\n", output); + backtrace_symbols_fd(callstackArray+offset, maxdepth, fd); + } else { + char **symbolStringList = backtrace_symbols(callstackArray, currentdepth); + if (symbolStringList) { + fputs("Callstack:\n", output); + char demangle_buffer[2048]= {0}; + for (int i = offset; i < maxdepth; ++i) { + const char * const symbolString = symbolStringList[i]; + char * realnameString = nullptr; + const char * const firstBracketName = strchr(symbolString, '('); + const char * const firstBracketAddress = strchr(symbolString, '['); + const char * const secondBracketAddress = strchr(firstBracketAddress, ']'); + const char * const beginAddress = firstBracketAddress+3; + const int addressLen = int(secondBracketAddress-beginAddress); + const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen); + if (demangling && firstBracketName) { + const char * const plus = strchr(firstBracketName, '+'); + if (plus && (plus>(firstBracketName+1))) { + char input_buffer[1024]= {0}; + strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1); + size_t length = getArrayLength(demangle_buffer); + int status=0; + // We're violating the specification - passing stack address instead of malloc'ed heap. + // Benefit is that no further heap is required, while there is sufficient stack... + realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success + } + } + const int ordinal=i-offset; + fprintf(output, "#%-2d 0x", + ordinal); + if (padLen>0) + fprintf(output, "%0*d", + padLen, 0); + if (realnameString) { + fprintf(output, "%.*s in %s\n", + (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, + realnameString); + } else { + fprintf(output, "%.*s in %.*s\n", + (int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3, + (int)(firstBracketAddress-symbolString), symbolString); + } + } + free(symbolStringList); + } else { + fputs("Callstack could not be obtained\n", output); + } + } +#undef ADDRESSDISPLAYLENGTH +} + +#endif // USE_UNIX_BACKTRACE_SUPPORT diff --git a/cli/stacktrace.h b/cli/stacktrace.h new file mode 100644 index 000000000..3922384d7 --- /dev/null +++ b/cli/stacktrace.h @@ -0,0 +1,38 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 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 . + */ + +#ifndef STACKTRACE_H +#define STACKTRACE_H + +#include "config.h" + +#ifdef USE_UNIX_BACKTRACE_SUPPORT + +#include + +/* + * Try to print the callstack. + * That is very sensitive to the operating system, hardware, compiler and runtime. + * The code is not meant for production environment! + * One reason is named first: it's using functions not whitelisted for usage in a signal handler function. + */ +void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem); + +#endif + +#endif // STACKTRACE_H diff --git a/lib/config.h b/lib/config.h index 6ee1ab024..60daf8e38 100644 --- a/lib/config.h +++ b/lib/config.h @@ -131,5 +131,16 @@ static const std::string emptyString; #define SUPPRESS_FLOAT_EQUAL_WARNING(...) __VA_ARGS__ #endif +#if defined(_WIN32) && defined(_MSC_VER) +#define USE_WINDOWS_SEH +#endif + +#if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__) +#define USE_UNIX_BACKTRACE_SUPPORT +#endif + +#if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__) +#define USE_UNIX_SIGNAL_HANDLING +#endif #endif // configH diff --git a/lib/utils.h b/lib/utils.h index 21993d637..396b44485 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -156,4 +156,14 @@ CPPCHECKLIB bool matchglobs(const std::vector &patterns, const std: CPPCHECKLIB void strTolower(std::string& str); +/** + * Simple helper function: + * \return size of array + * */ +template +std::size_t getArrayLength(const T (&)[size]) +{ + return size; +} + #endif diff --git a/test/testrunner.vcxproj b/test/testrunner.vcxproj index 478dab868..fbe501e72 100755 --- a/test/testrunner.vcxproj +++ b/test/testrunner.vcxproj @@ -26,6 +26,7 @@ + diff --git a/tools/dmake.cpp b/tools/dmake.cpp index 500c77776..e3932cc0f 100644 --- a/tools/dmake.cpp +++ b/tools/dmake.cpp @@ -415,7 +415,7 @@ int main(int argc, char **argv) fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "all:\tcppcheck testrunner\n\n"; - fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n"; + fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o\n"; fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n"; fout << "test:\tall\n"; fout << "\t./testrunner\n\n";