Cppcheck
cppcheckexecutorsig.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2023 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 "cppcheckexecutorsig.h"
20 
21 #if defined(USE_UNIX_SIGNAL_HANDLING)
22 
23 #include "cppcheckexecutor.h"
24 
25 #ifdef USE_UNIX_BACKTRACE_SUPPORT
26 #include "stacktrace.h"
27 #endif
28 
29 #include <csignal>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 //#include <features.h> // __USE_DYNAMIC_STACK_SIZE
34 #include <map>
35 #include <string>
36 #include <unistd.h>
37 #include <utility>
38 
39 #if defined(__linux__) && defined(REG_ERR)
40 #include <sys/syscall.h>
41 #endif
42 
43 #if defined(__APPLE__)
44 # define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined
45 # include <ucontext.h>
46 
47 # undef _XOPEN_SOURCE
48 #elif !defined(__OpenBSD__) && !defined(__HAIKU__)
49 # include <ucontext.h>
50 #endif
51 
52 // 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
53 #ifdef __USE_DYNAMIC_STACK_SIZE
54 static constexpr size_t MYSTACKSIZE = 16*1024+32768; // wild guess about a reasonable buffer
55 #else
56 static constexpr size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
57 #endif
58 static char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler
59 static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper()
60 
61 /**
62  * \param[in] ptr address to be examined.
63  * \return true if address is supposed to be on stack (contrary to heap). If ptr is 0 false will be returned.
64  * If unknown better return false.
65  */
66 static bool IsAddressOnStack(const void* ptr)
67 {
68  if (nullptr==ptr)
69  return false;
70  char a;
71  if (bStackBelowHeap)
72  return ptr < &a;
73  return ptr > &a;
74 }
75 
76 /* (declare this list here, so it may be used in signal handlers in addition to main())
77  * A list of signals available in ISO C
78  * Check out http://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
79  * For now we only want to detect abnormal behaviour for a few selected signals:
80  */
81 
82 #define DECLARE_SIGNAL(x) std::make_pair(x, #x)
83 using Signalmap_t = std::map<int, std::string>;
84 static const Signalmap_t listofsignals = {
85  DECLARE_SIGNAL(SIGABRT),
86  DECLARE_SIGNAL(SIGBUS),
87  DECLARE_SIGNAL(SIGFPE),
88  DECLARE_SIGNAL(SIGILL),
89  DECLARE_SIGNAL(SIGINT),
90  DECLARE_SIGNAL(SIGQUIT),
91  DECLARE_SIGNAL(SIGSEGV),
92  DECLARE_SIGNAL(SIGSYS),
93  // don't care: SIGTERM
94  DECLARE_SIGNAL(SIGUSR1),
95  //DECLARE_SIGNAL(SIGUSR2) no usage currently
96 };
97 #undef DECLARE_SIGNAL
98 /*
99  * Entry pointer for signal handlers
100  * It uses functions which are not safe to be called from a signal handler,
101  * (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist)
102  * but when ending up here something went terribly wrong anyway.
103  * And all which is left is just printing some information and terminate.
104  */
105 // cppcheck-suppress constParameterCallback
106 static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
107 {
108  int type = -1;
109  pid_t killid;
110  // TODO: separate these two defines
111 #if defined(__linux__) && defined(REG_ERR)
112  const auto* const uc = reinterpret_cast<const ucontext_t*>(context);
113  killid = (pid_t) syscall(SYS_gettid);
114  if (uc) {
115  type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2;
116  }
117 #else
118  (void)context;
119  killid = getpid();
120 #endif
121 
122  const Signalmap_t::const_iterator it=listofsignals.find(signo);
123  const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str();
124 #ifdef USE_UNIX_BACKTRACE_SUPPORT
125  bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then.
126 #endif
127  bool unexpectedSignal=true; // unexpected indicates program failure
128  bool terminate=true; // exit process/thread
129  const bool isAddressOnStack = IsAddressOnStack(info->si_addr);
130  FILE* output = CppCheckExecutor::getExceptionOutput();
131  switch (signo) {
132  case SIGABRT:
133  fputs("Internal error: cppcheck received signal ", output);
134  fputs(signame, output);
135  fputs(
136 #ifdef NDEBUG
137  " - out of memory?\n",
138 #else
139  " - out of memory or assertion?\n",
140 #endif
141  output);
142 #ifdef USE_UNIX_BACKTRACE_SUPPORT
143  lowMem=true; // educated guess
144 #endif
145  break;
146  case SIGBUS:
147  fputs("Internal error: cppcheck received signal ", output);
148  fputs(signame, output);
149  switch (info->si_code) {
150  case BUS_ADRALN: // invalid address alignment
151  fputs(" - BUS_ADRALN", output);
152  break;
153  case BUS_ADRERR: // nonexistent physical address
154  fputs(" - BUS_ADRERR", output);
155  break;
156  case BUS_OBJERR: // object-specific hardware error
157  fputs(" - BUS_OBJERR", output);
158  break;
159 #ifdef BUS_MCEERR_AR
160  case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check;
161  fputs(" - BUS_MCEERR_AR", output);
162  break;
163 #endif
164 #ifdef BUS_MCEERR_AO
165  case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed
166  fputs(" - BUS_MCEERR_AO", output);
167  break;
168 #endif
169  default:
170  break;
171  }
172  fprintf(output, " (at 0x%lx).\n",
173  (unsigned long)info->si_addr);
174  break;
175  case SIGFPE:
176  fputs("Internal error: cppcheck received signal ", output);
177  fputs(signame, output);
178  switch (info->si_code) {
179  case FPE_INTDIV: // integer divide by zero
180  fputs(" - FPE_INTDIV", output);
181  break;
182  case FPE_INTOVF: // integer overflow
183  fputs(" - FPE_INTOVF", output);
184  break;
185  case FPE_FLTDIV: // floating-point divide by zero
186  fputs(" - FPE_FLTDIV", output);
187  break;
188  case FPE_FLTOVF: // floating-point overflow
189  fputs(" - FPE_FLTOVF", output);
190  break;
191  case FPE_FLTUND: // floating-point underflow
192  fputs(" - FPE_FLTUND", output);
193  break;
194  case FPE_FLTRES: // floating-point inexact result
195  fputs(" - FPE_FLTRES", output);
196  break;
197  case FPE_FLTINV: // floating-point invalid operation
198  fputs(" - FPE_FLTINV", output);
199  break;
200  case FPE_FLTSUB: // subscript out of range
201  fputs(" - FPE_FLTSUB", output);
202  break;
203  default:
204  break;
205  }
206  fprintf(output, " (at 0x%lx).\n",
207  (unsigned long)info->si_addr);
208  break;
209  case SIGILL:
210  fputs("Internal error: cppcheck received signal ", output);
211  fputs(signame, output);
212  switch (info->si_code) {
213  case ILL_ILLOPC: // illegal opcode
214  fputs(" - ILL_ILLOPC", output);
215  break;
216  case ILL_ILLOPN: // illegal operand
217  fputs(" - ILL_ILLOPN", output);
218  break;
219  case ILL_ILLADR: // illegal addressing mode
220  fputs(" - ILL_ILLADR", output);
221  break;
222  case ILL_ILLTRP: // illegal trap
223  fputs(" - ILL_ILLTRP", output);
224  break;
225  case ILL_PRVOPC: // privileged opcode
226  fputs(" - ILL_PRVOPC", output);
227  break;
228  case ILL_PRVREG: // privileged register
229  fputs(" - ILL_PRVREG", output);
230  break;
231  case ILL_COPROC: // coprocessor error
232  fputs(" - ILL_COPROC", output);
233  break;
234  case ILL_BADSTK: // internal stack error
235  fputs(" - ILL_BADSTK", output);
236  break;
237  default:
238  break;
239  }
240  fprintf(output, " (at 0x%lx).%s\n",
241  (unsigned long)info->si_addr,
242  (isAddressOnStack)?" Stackoverflow?":"");
243  break;
244  case SIGINT:
245  unexpectedSignal=false; // legal usage: interrupt application via CTRL-C
246  fputs("cppcheck received signal ", output);
247  fputs(signame, output);
248  fputs(".\n", output);
249  break;
250  case SIGSEGV:
251  fputs("Internal error: cppcheck received signal ", output);
252  fputs(signame, output);
253  switch (info->si_code) {
254  case SEGV_MAPERR: // address not mapped to object
255  fputs(" - SEGV_MAPERR", output);
256  break;
257  case SEGV_ACCERR: // invalid permissions for mapped object
258  fputs(" - SEGV_ACCERR", output);
259  break;
260  default:
261  break;
262  }
263  fprintf(output, " (%sat 0x%lx).%s\n",
264  // cppcheck-suppress knownConditionTrueFalse ; FP
265  (type==-1)? "" :
266  (type==0) ? "reading " : "writing ",
267  (unsigned long)info->si_addr,
268  (isAddressOnStack)?" Stackoverflow?":""
269  );
270  break;
271  case SIGUSR1:
272  fputs("cppcheck received signal ", output);
273  fputs(signame, output);
274  fputs(".\n", output);
275  terminate=false;
276  break;
277  default:
278  fputs("Internal error: cppcheck received signal ", output);
279  fputs(signame, output);
280  fputs(".\n", output);
281  break;
282  }
283 #ifdef USE_UNIX_BACKTRACE_SUPPORT
284  print_stacktrace(output, true, -1, lowMem);
285 #endif
286  if (unexpectedSignal) {
287  fputs("\nPlease report this to the cppcheck developers!\n", output);
288  }
289  fflush(output);
290 
291  if (terminate) {
292  // now let things proceed, shutdown and hopefully dump core for post-mortem analysis
293  struct sigaction act;
294  memset(&act, 0, sizeof(act));
295  act.sa_handler=SIG_DFL;
296  sigaction(signo, &act, nullptr);
297  kill(killid, signo);
298  }
299 }
300 
301 int check_wrapper_sig(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(const Settings&) const, const Settings& settings)
302 {
303  // determine stack vs. heap
304  char stackVariable;
305  char *heapVariable=static_cast<char*>(malloc(1));
306  bStackBelowHeap = &stackVariable < heapVariable;
307  free(heapVariable);
308 
309  // set up alternative stack for signal handler
310  stack_t segv_stack;
311  segv_stack.ss_sp = mytstack;
312  segv_stack.ss_flags = 0;
313  segv_stack.ss_size = MYSTACKSIZE;
314  sigaltstack(&segv_stack, nullptr);
315 
316  // install signal handler
317  struct sigaction act;
318  memset(&act, 0, sizeof(act));
319  act.sa_flags=SA_SIGINFO|SA_ONSTACK;
320  act.sa_sigaction=CppcheckSignalHandler;
321  for (std::map<int, std::string>::const_iterator sig=listofsignals.cbegin(); sig!=listofsignals.cend(); ++sig) {
322  sigaction(sig->first, &act, nullptr);
323  }
324  return (executor.*f)(settings);
325 }
326 
327 #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:93