270 lines
10 KiB
C++
270 lines
10 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "cppcheckexecutorseh.h"
|
|
|
|
#ifdef USE_WINDOWS_SEH
|
|
|
|
#include "cppcheckexecutor.h"
|
|
#include "utils.h"
|
|
|
|
#include <windows.h>
|
|
#include <dbghelp.h>
|
|
#include <tchar.h>
|
|
|
|
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<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
|
break;
|
|
case 1:
|
|
fprintf(outputFile, "writing to 0x%p",
|
|
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
|
break;
|
|
case 8:
|
|
fprintf(outputFile, "data execution prevention at 0x%p",
|
|
reinterpret_cast<void*>(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
|