Cppcheck
cppcheckexecutorseh.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "cppcheckexecutorseh.h"
20 
21 #ifdef USE_WINDOWS_SEH
22 
23 #include "cppcheckexecutor.h"
24 #include "utils.h"
25 
26 #include <windows.h>
27 #include <dbghelp.h>
28 #include <tchar.h>
29 
30 namespace {
31  const ULONG maxnamelength = 512;
32  struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 {
33  TCHAR nameExt[maxnamelength]; // actually no need to worry about character encoding here
34  };
35  typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
36  fpStackWalk64 pStackWalk64;
37  typedef DWORD64 (WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
38  fpSymGetModuleBase64 pSymGetModuleBase64;
39  typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
40  fpSymGetSymFromAddr64 pSymGetSymFromAddr64;
41  typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
42  fpSymGetLineFromAddr64 pSymGetLineFromAddr64;
43  typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD);
44  fpUnDecorateSymbolName pUnDecorateSymbolName;
45  typedef PVOID (WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
46  fpSymFunctionTableAccess64 pSymFunctionTableAccess64;
47  typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);
48  fpSymInitialize pSymInitialize;
49 
50  HMODULE hLibDbgHelp;
51 // avoid explicit dependency on Dbghelp.dll
52  bool loadDbgHelp()
53  {
54  hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll");
55  if (!hLibDbgHelp)
56  return false;
57  pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64");
58  pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64");
59  pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64");
60  pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64");
61  pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64");
62  pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize");
63  pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName");
64  return true;
65  }
66 
67 
68  void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex)
69  {
70  if (!loadDbgHelp())
71  return;
72  const HANDLE hProcess = GetCurrentProcess();
73  const HANDLE hThread = GetCurrentThread();
74  pSymInitialize(
75  hProcess,
76  nullptr,
77  TRUE
78  );
79  CONTEXT context = *(ex->ContextRecord);
80  STACKFRAME64 stack= {0};
81 #ifdef _M_IX86
82  stack.AddrPC.Offset = context.Eip;
83  stack.AddrPC.Mode = AddrModeFlat;
84  stack.AddrStack.Offset = context.Esp;
85  stack.AddrStack.Mode = AddrModeFlat;
86  stack.AddrFrame.Offset = context.Ebp;
87  stack.AddrFrame.Mode = AddrModeFlat;
88 #else
89  stack.AddrPC.Offset = context.Rip;
90  stack.AddrPC.Mode = AddrModeFlat;
91  stack.AddrStack.Offset = context.Rsp;
92  stack.AddrStack.Mode = AddrModeFlat;
93  stack.AddrFrame.Offset = context.Rsp;
94  stack.AddrFrame.Mode = AddrModeFlat;
95 #endif
96  IMAGEHLP_SYMBOL64_EXT symbol;
97  symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
98  symbol.MaxNameLength = maxnamelength;
99  DWORD64 displacement = 0;
100  int beyond_main=-1; // emergency exit, see below
101  for (ULONG frame = 0; ; frame++) {
102  BOOL result = pStackWalk64
103  (
104 #ifdef _M_IX86
105  IMAGE_FILE_MACHINE_I386,
106 #else
107  IMAGE_FILE_MACHINE_AMD64,
108 #endif
109  hProcess,
110  hThread,
111  &stack,
112  &context,
113  nullptr,
114  pSymFunctionTableAccess64,
115  pSymGetModuleBase64,
116  nullptr
117  );
118  if (!result) // official end...
119  break;
120  pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol);
121  TCHAR undname[maxnamelength]= {0};
122  pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE);
123  if (beyond_main>=0)
124  ++beyond_main;
125  if (_tcscmp(undname, _T("main"))==0)
126  beyond_main=0;
127  fprintf(outputFile,
128  "%lu. 0x%08I64X in ",
129  frame, (ULONG64)stack.AddrPC.Offset);
130  fputs((const char *)undname, outputFile);
131  fputc('\n', outputFile);
132  if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end...
133  break;
134  }
135 
136  FreeLibrary(hLibDbgHelp);
137  hLibDbgHelp=nullptr;
138  }
139 
140  void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description)
141  {
142  fputs(description, outputFile);
143  fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress);
144  // Using %p for ULONG_PTR later on, so it must have size identical to size of pointer
145  // This is not the universally portable solution but good enough for Win32/64
146  C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1]));
147  switch (ex->ExceptionRecord->ExceptionInformation[0]) {
148  case 0:
149  fprintf(outputFile, "reading from 0x%p",
150  reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
151  break;
152  case 1:
153  fprintf(outputFile, "writing to 0x%p",
154  reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
155  break;
156  case 8:
157  fprintf(outputFile, "data execution prevention at 0x%p",
158  reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
159  break;
160  default:
161  break;
162  }
163  }
164 
165  /*
166  * Any evaluation of the exception needs to be done here!
167  */
168  int filterException(FILE *outputFile, int code, PEXCEPTION_POINTERS ex)
169  {
170  fputs("Internal error: ", outputFile);
171  switch (ex->ExceptionRecord->ExceptionCode) {
172  case EXCEPTION_ACCESS_VIOLATION:
173  writeMemoryErrorDetails(outputFile, ex, "Access violation");
174  break;
175  case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
176  fputs("Out of array bounds", outputFile);
177  break;
178  case EXCEPTION_BREAKPOINT:
179  fputs("Breakpoint", outputFile);
180  break;
181  case EXCEPTION_DATATYPE_MISALIGNMENT:
182  fputs("Misaligned data", outputFile);
183  break;
184  case EXCEPTION_FLT_DENORMAL_OPERAND:
185  fputs("Denormalized floating-point value", outputFile);
186  break;
187  case EXCEPTION_FLT_DIVIDE_BY_ZERO:
188  fputs("Floating-point divide-by-zero", outputFile);
189  break;
190  case EXCEPTION_FLT_INEXACT_RESULT:
191  fputs("Inexact floating-point value", outputFile);
192  break;
193  case EXCEPTION_FLT_INVALID_OPERATION:
194  fputs("Invalid floating-point operation", outputFile);
195  break;
196  case EXCEPTION_FLT_OVERFLOW:
197  fputs("Floating-point overflow", outputFile);
198  break;
199  case EXCEPTION_FLT_STACK_CHECK:
200  fputs("Floating-point stack overflow", outputFile);
201  break;
202  case EXCEPTION_FLT_UNDERFLOW:
203  fputs("Floating-point underflow", outputFile);
204  break;
205  case EXCEPTION_GUARD_PAGE:
206  fputs("Page-guard access", outputFile);
207  break;
208  case EXCEPTION_ILLEGAL_INSTRUCTION:
209  fputs("Illegal instruction", outputFile);
210  break;
211  case EXCEPTION_IN_PAGE_ERROR:
212  writeMemoryErrorDetails(outputFile, ex, "Invalid page access");
213  break;
214  case EXCEPTION_INT_DIVIDE_BY_ZERO:
215  fputs("Integer divide-by-zero", outputFile);
216  break;
217  case EXCEPTION_INT_OVERFLOW:
218  fputs("Integer overflow", outputFile);
219  break;
220  case EXCEPTION_INVALID_DISPOSITION:
221  fputs("Invalid exception dispatcher", outputFile);
222  break;
223  case EXCEPTION_INVALID_HANDLE:
224  fputs("Invalid handle", outputFile);
225  break;
226  case EXCEPTION_NONCONTINUABLE_EXCEPTION:
227  fputs("Non-continuable exception", outputFile);
228  break;
229  case EXCEPTION_PRIV_INSTRUCTION:
230  fputs("Invalid instruction", outputFile);
231  break;
232  case EXCEPTION_SINGLE_STEP:
233  fputs("Single instruction step", outputFile);
234  break;
235  case EXCEPTION_STACK_OVERFLOW:
236  fputs("Stack overflow", outputFile);
237  break;
238  default:
239  fprintf(outputFile, "Unknown exception (%d)\n",
240  code);
241  break;
242  }
243  fputc('\n', outputFile);
244  printCallstack(outputFile, ex);
245  fflush(outputFile);
246  return EXCEPTION_EXECUTE_HANDLER;
247  }
248 }
249 
250 /**
251  * Signal/SEH handling
252  * Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed!
253  * TODO Check for multi-threading issues!
254  *
255  */
256 int check_wrapper_seh(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(const Settings&) const, const Settings& settings)
257 {
258  FILE *outputFile = CppCheckExecutor::getExceptionOutput();
259  __try {
260  return (&executor->*f)(settings);
261  } __except (filterException(outputFile, GetExceptionCode(), GetExceptionInformation())) {
262  fputs("Please report this to the cppcheck developers!\n", outputFile);
263  return -1;
264  }
265 }
266 
267 #endif
This class works as an example of how CppCheck can be used in external programs without very little k...
static FILE * getExceptionOutput()
This is just a container for general settings so that we don't need to pass individual values to func...
Definition: settings.h:95
std::size_t getArrayLength(const T(&)[size])
Simple helper function:
Definition: utils.h:296