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