GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/main/signal_handlers.cpp Lines: 39 122 32.0 %
Date: 2021-03-23 Branches: 15 120 12.5 %

Line Exec Source
1
/*********************                                                        */
2
/*! \file signal_handlers.cpp
3
 ** \verbatim
4
 ** Top contributors (to current version):
5
 **   Morgan Deters, Gereon Kremer, Andres Noetzli
6
 ** This file is part of the CVC4 project.
7
 ** Copyright (c) 2009-2021 by the authors listed in the file AUTHORS
8
 ** in the top-level source directory and their institutional affiliations.
9
 ** All rights reserved.  See the file COPYING in the top-level source
10
 ** directory for licensing information.\endverbatim
11
 **
12
 ** \brief Implementation of signal handlers.
13
 **
14
 ** Implementation of signal handlers.
15
 **
16
 ** It is important to only call async-signal-safe functions from signal
17
 ** handlers. See: http://man7.org/linux/man-pages/man7/signal-safety.7.html for
18
 ** a list of async-signal-safe POSIX.1 functions.
19
 **/
20
21
#include <string.h>
22
23
#include <cerrno>
24
#include <cstdio>
25
#include <cstdlib>
26
#include <exception>
27
28
#ifndef __WIN32__
29
30
#include <signal.h>
31
#include <sys/resource.h>
32
#include <unistd.h>
33
34
#endif /* __WIN32__ */
35
36
#include "base/exception.h"
37
#include "cvc4autoconfig.h"
38
#include "main/command_executor.h"
39
#include "main/main.h"
40
#include "options/options.h"
41
#include "smt/smt_engine.h"
42
#include "util/safe_print.h"
43
44
using CVC4::Exception;
45
using namespace std;
46
47
namespace CVC4 {
48
namespace main {
49
50
/**
51
 * If true, will not spin on segfault even when CVC4_DEBUG is on.
52
 * Useful for nightly regressions, noninteractive performance runs
53
 * etc.
54
 */
55
bool segvSpin = false;
56
57
namespace signal_handlers {
58
59
void print_statistics()
60
{
61
  if (pOptions != NULL && pOptions->getStatistics() && pExecutor != NULL)
62
  {
63
    pExecutor->safeFlushStatistics(STDERR_FILENO);
64
  }
65
}
66
67
void timeout_handler()
68
{
69
  safe_print(STDERR_FILENO, "CVC4 interrupted by timeout.\n");
70
  print_statistics();
71
  abort();
72
}
73
74
#ifndef __WIN32__
75
76
#ifdef HAVE_SIGALTSTACK
77
size_t cvc4StackSize;
78
void* cvc4StackBase;
79
#endif /* HAVE_SIGALTSTACK */
80
81
/** Handler for SIGXCPU and SIGALRM, i.e., timeout. */
82
void timeout_handler(int sig, siginfo_t* info, void*) { timeout_handler(); }
83
84
/** Handler for SIGTERM. */
85
void sigterm_handler(int sig, siginfo_t* info, void*)
86
{
87
  safe_print(STDERR_FILENO, "CVC4 interrupted by SIGTERM.\n");
88
  print_statistics();
89
  abort();
90
}
91
92
/** Handler for SIGINT, i.e., when the user hits control C. */
93
void sigint_handler(int sig, siginfo_t* info, void*)
94
{
95
  safe_print(STDERR_FILENO, "CVC4 interrupted by user.\n");
96
  print_statistics();
97
  abort();
98
}
99
100
#ifdef HAVE_SIGALTSTACK
101
/** Handler for SIGSEGV (segfault). */
102
void segv_handler(int sig, siginfo_t* info, void* c)
103
{
104
  uintptr_t extent = reinterpret_cast<uintptr_t>(cvc4StackBase) - cvc4StackSize;
105
  uintptr_t addr = reinterpret_cast<uintptr_t>(info->si_addr);
106
#ifdef CVC4_DEBUG
107
  safe_print(STDERR_FILENO, "CVC4 suffered a segfault in DEBUG mode.\n");
108
  safe_print(STDERR_FILENO, "Offending address is ");
109
  safe_print(STDERR_FILENO, info->si_addr);
110
  safe_print(STDERR_FILENO, "\n");
111
  // cerr << "base is " << (void*)cvc4StackBase << endl;
112
  // cerr << "size is " << cvc4StackSize << endl;
113
  // cerr << "extent is " << (void*)extent << endl;
114
  if (addr >= extent && addr <= extent + 10 * 1024)
115
  {
116
    safe_print(STDERR_FILENO,
117
               "Looks like this is likely due to stack overflow.\n");
118
    safe_print(STDERR_FILENO,
119
               "You might consider increasing the limit with `ulimit -s' or "
120
               "equivalent.\n");
121
  }
122
  else if (addr < 10 * 1024)
123
  {
124
    safe_print(STDERR_FILENO, "Looks like a NULL pointer was dereferenced.\n");
125
  }
126
127
  if (!segvSpin)
128
  {
129
    print_statistics();
130
    abort();
131
  }
132
  else
133
  {
134
    safe_print(STDERR_FILENO,
135
               "Spinning so that a debugger can be connected.\n");
136
    safe_print(STDERR_FILENO, "Try:  gdb ");
137
    safe_print(STDERR_FILENO, *progName);
138
    safe_print(STDERR_FILENO, " ");
139
    safe_print<int64_t>(STDERR_FILENO, getpid());
140
    safe_print(STDERR_FILENO, "\n");
141
    safe_print(STDERR_FILENO, " or:  gdb --pid=");
142
    safe_print<int64_t>(STDERR_FILENO, getpid());
143
    safe_print(STDERR_FILENO, " ");
144
    safe_print(STDERR_FILENO, *progName);
145
    safe_print(STDERR_FILENO, "\n");
146
    for (;;)
147
    {
148
      sleep(60);
149
    }
150
  }
151
#else  /* CVC4_DEBUG */
152
  safe_print(STDERR_FILENO, "CVC4 suffered a segfault.\n");
153
  safe_print(STDERR_FILENO, "Offending address is ");
154
  safe_print(STDERR_FILENO, info->si_addr);
155
  safe_print(STDERR_FILENO, "\n");
156
  if (addr >= extent && addr <= extent + 10 * 1024)
157
  {
158
    safe_print(STDERR_FILENO,
159
               "Looks like this is likely due to stack overflow.\n");
160
    safe_print(STDERR_FILENO,
161
               "You might consider increasing the limit with `ulimit -s' or "
162
               "equivalent.\n");
163
  }
164
  else if (addr < 10 * 1024)
165
  {
166
    safe_print(STDERR_FILENO, "Looks like a NULL pointer was dereferenced.\n");
167
  }
168
  print_statistics();
169
  abort();
170
#endif /* CVC4_DEBUG */
171
}
172
#endif /* HAVE_SIGALTSTACK */
173
174
/** Handler for SIGILL (illegal instruction). */
175
void ill_handler(int sig, siginfo_t* info, void*)
176
{
177
#ifdef CVC4_DEBUG
178
  safe_print(STDERR_FILENO,
179
             "CVC4 executed an illegal instruction in DEBUG mode.\n");
180
  if (!segvSpin)
181
  {
182
    print_statistics();
183
    abort();
184
  }
185
  else
186
  {
187
    safe_print(STDERR_FILENO,
188
               "Spinning so that a debugger can be connected.\n");
189
    safe_print(STDERR_FILENO, "Try:  gdb ");
190
    safe_print(STDERR_FILENO, *progName);
191
    safe_print(STDERR_FILENO, " ");
192
    safe_print<int64_t>(STDERR_FILENO, getpid());
193
    safe_print(STDERR_FILENO, "\n");
194
    safe_print(STDERR_FILENO, " or:  gdb --pid=");
195
    safe_print<int64_t>(STDERR_FILENO, getpid());
196
    safe_print(STDERR_FILENO, " ");
197
    safe_print(STDERR_FILENO, *progName);
198
    safe_print(STDERR_FILENO, "\n");
199
    for (;;)
200
    {
201
      sleep(60);
202
    }
203
  }
204
#else  /* CVC4_DEBUG */
205
  safe_print(STDERR_FILENO, "CVC4 executed an illegal instruction.\n");
206
  print_statistics();
207
  abort();
208
#endif /* CVC4_DEBUG */
209
}
210
211
#endif /* __WIN32__ */
212
213
static terminate_handler default_terminator;
214
215
void cvc4terminate()
216
{
217
  set_terminate(default_terminator);
218
#ifdef CVC4_DEBUG
219
  LastExceptionBuffer* current = LastExceptionBuffer::getCurrent();
220
  LastExceptionBuffer::setCurrent(NULL);
221
  delete current;
222
223
  safe_print(STDERR_FILENO,
224
             "\n"
225
             "CVC4 was terminated by the C++ runtime.\n"
226
             "Perhaps an exception was thrown during stack unwinding.  "
227
             "(Don't do that.)\n");
228
  print_statistics();
229
  default_terminator();
230
#else  /* CVC4_DEBUG */
231
  safe_print(STDERR_FILENO,
232
             "CVC4 was terminated by the C++ runtime.\n"
233
             "Perhaps an exception was thrown during stack unwinding.\n");
234
  print_statistics();
235
  default_terminator();
236
#endif /* CVC4_DEBUG */
237
}
238
239
7909
void install()
240
{
241
#ifdef CVC4_DEBUG
242
7909
  LastExceptionBuffer::setCurrent(new LastExceptionBuffer());
243
#endif
244
245
#ifndef __WIN32__
246
  struct rlimit limit;
247
7909
  if (getrlimit(RLIMIT_STACK, &limit))
248
  {
249
    throw Exception(string("getrlimit() failure: ") + strerror(errno));
250
  }
251
7909
  if (limit.rlim_cur != limit.rlim_max)
252
  {
253
    limit.rlim_cur = limit.rlim_max;
254
    if (setrlimit(RLIMIT_STACK, &limit))
255
    {
256
      throw Exception(string("setrlimit() failure: ") + strerror(errno));
257
    }
258
    if (getrlimit(RLIMIT_STACK, &limit))
259
    {
260
      throw Exception(string("getrlimit() failure: ") + strerror(errno));
261
    }
262
  }
263
264
  struct sigaction act1;
265
7909
  act1.sa_sigaction = sigint_handler;
266
7909
  act1.sa_flags = SA_SIGINFO;
267
7909
  sigemptyset(&act1.sa_mask);
268
7909
  if (sigaction(SIGINT, &act1, NULL))
269
  {
270
    throw Exception(string("sigaction(SIGINT) failure: ") + strerror(errno));
271
  }
272
273
  struct sigaction act2;
274
7909
  act2.sa_sigaction = timeout_handler;
275
7909
  act2.sa_flags = SA_SIGINFO;
276
7909
  sigemptyset(&act2.sa_mask);
277
7909
  if (sigaction(SIGXCPU, &act2, NULL))
278
  {
279
    throw Exception(string("sigaction(SIGXCPU) failure: ") + strerror(errno));
280
  }
281
282
  struct sigaction act3;
283
7909
  act3.sa_sigaction = ill_handler;
284
7909
  act3.sa_flags = SA_SIGINFO;
285
7909
  sigemptyset(&act3.sa_mask);
286
7909
  if (sigaction(SIGILL, &act3, NULL))
287
  {
288
    throw Exception(string("sigaction(SIGILL) failure: ") + strerror(errno));
289
  }
290
291
#ifdef HAVE_SIGALTSTACK
292
  stack_t ss;
293
7909
  ss.ss_sp = (char*)malloc(SIGSTKSZ);
294
7909
  if (ss.ss_sp == NULL)
295
  {
296
    throw Exception("Can't malloc() space for a signal stack");
297
  }
298
7909
  ss.ss_size = SIGSTKSZ;
299
7909
  ss.ss_flags = 0;
300
7909
  if (sigaltstack(&ss, NULL) == -1)
301
  {
302
    throw Exception(string("sigaltstack() failure: ") + strerror(errno));
303
  }
304
305
7909
  cvc4StackSize = limit.rlim_cur;
306
7909
  cvc4StackBase = ss.ss_sp;
307
308
  struct sigaction act4;
309
7909
  act4.sa_sigaction = segv_handler;
310
7909
  act4.sa_flags = SA_SIGINFO | SA_ONSTACK;
311
7909
  sigemptyset(&act4.sa_mask);
312
7909
  if (sigaction(SIGSEGV, &act4, NULL))
313
  {
314
    throw Exception(string("sigaction(SIGSEGV) failure: ") + strerror(errno));
315
  }
316
#endif /* HAVE_SIGALTSTACK */
317
318
  struct sigaction act5;
319
7909
  act5.sa_sigaction = sigterm_handler;
320
7909
  act5.sa_flags = SA_SIGINFO;
321
7909
  sigemptyset(&act5.sa_mask);
322
7909
  if (sigaction(SIGTERM, &act5, NULL))
323
  {
324
    throw Exception(string("sigaction(SIGTERM) failure: ") + strerror(errno));
325
  }
326
327
#endif /* __WIN32__ */
328
329
7909
  default_terminator = set_terminate(cvc4terminate);
330
7909
}
331
332
5438
void cleanup() noexcept
333
{
334
#ifndef __WIN32__
335
#ifdef HAVE_SIGALTSTACK
336
5438
  free(cvc4StackBase);
337
5438
  cvc4StackBase = NULL;
338
5438
  cvc4StackSize = 0;
339
#endif /* HAVE_SIGALTSTACK */
340
#endif /* __WIN32__ */
341
5438
}
342
343
}  // namespace signal_handlers
344
}  // namespace main
345
35178
}  // namespace CVC4