/****************************************************************************** * Top contributors (to current version): * Morgan Deters, Gereon Kremer, Andres Noetzli * * This file is part of the cvc5 project. * * Copyright (c) 2009-2021 by the authors listed in the file AUTHORS * in the top-level source directory and their institutional affiliations. * All rights reserved. See the file COPYING in the top-level source * directory for licensing information. * **************************************************************************** * * Implementation of signal handlers. * * It is important to only call async-signal-safe functions from signal * handlers. See: http://man7.org/linux/man-pages/man7/signal-safety.7.html for * a list of async-signal-safe POSIX.1 functions. */ #include #include #include #include #include #ifndef __WIN32__ #include #include #include #endif /* __WIN32__ */ #include "base/cvc5config.h" #include "base/exception.h" #include "main/command_executor.h" #include "main/main.h" #include "options/options.h" #include "util/safe_print.h" using cvc5::Exception; using namespace std; namespace cvc5 { namespace main { /** * If true, will not spin on segfault even when CVC5_DEBUG is on. * Useful for nightly regressions, noninteractive performance runs * etc. */ bool segvSpin = false; namespace signal_handlers { void print_statistics() { if (pExecutor != nullptr) { totalTime.reset(); pExecutor->printStatisticsSafe(STDERR_FILENO); } } void timeout_handler() { safe_print(STDERR_FILENO, "cvc5 interrupted by timeout.\n"); print_statistics(); abort(); } #ifndef __WIN32__ #ifdef HAVE_SIGALTSTACK size_t stackSize; void* stackBase; #endif /* HAVE_SIGALTSTACK */ /** Handler for SIGXCPU and SIGALRM, i.e., timeout. */ void timeout_handler(int sig, siginfo_t* info, void*) { timeout_handler(); } /** Handler for SIGTERM. */ void sigterm_handler(int sig, siginfo_t* info, void*) { safe_print(STDERR_FILENO, "cvc5 interrupted by SIGTERM.\n"); print_statistics(); signal(sig, SIG_DFL); raise(sig); } /** Handler for SIGINT, i.e., when the user hits control C. */ void sigint_handler(int sig, siginfo_t* info, void*) { safe_print(STDERR_FILENO, "cvc5 interrupted by user.\n"); print_statistics(); signal(sig, SIG_DFL); raise(sig); } #ifdef HAVE_SIGALTSTACK /** Handler for SIGSEGV (segfault). */ void segv_handler(int sig, siginfo_t* info, void* c) { uintptr_t extent = reinterpret_cast(stackBase) - stackSize; uintptr_t addr = reinterpret_cast(info->si_addr); #ifdef CVC5_DEBUG safe_print(STDERR_FILENO, "cvc5 suffered a segfault in DEBUG mode.\n"); safe_print(STDERR_FILENO, "Offending address is "); safe_print(STDERR_FILENO, info->si_addr); safe_print(STDERR_FILENO, "\n"); // cerr << "base is " << (void*)stackBase << endl; // cerr << "size is " << stackSize << endl; // cerr << "extent is " << (void*)extent << endl; if (addr >= extent && addr <= extent + 10 * 1024) { safe_print(STDERR_FILENO, "Looks like this is likely due to stack overflow.\n"); safe_print(STDERR_FILENO, "You might consider increasing the limit with `ulimit -s' or " "equivalent.\n"); } else if (addr < 10 * 1024) { safe_print(STDERR_FILENO, "Looks like a NULL pointer was dereferenced.\n"); } if (!segvSpin) { print_statistics(); signal(sig, SIG_DFL); raise(sig); } else { safe_print(STDERR_FILENO, "Spinning so that a debugger can be connected.\n"); safe_print(STDERR_FILENO, "Try: gdb "); safe_print(STDERR_FILENO, *progName); safe_print(STDERR_FILENO, " "); safe_print(STDERR_FILENO, getpid()); safe_print(STDERR_FILENO, "\n"); safe_print(STDERR_FILENO, " or: gdb --pid="); safe_print(STDERR_FILENO, getpid()); safe_print(STDERR_FILENO, " "); safe_print(STDERR_FILENO, *progName); safe_print(STDERR_FILENO, "\n"); for (;;) { sleep(60); } } #else /* CVC5_DEBUG */ safe_print(STDERR_FILENO, "cvc5 suffered a segfault.\n"); safe_print(STDERR_FILENO, "Offending address is "); safe_print(STDERR_FILENO, info->si_addr); safe_print(STDERR_FILENO, "\n"); if (addr >= extent && addr <= extent + 10 * 1024) { safe_print(STDERR_FILENO, "Looks like this is likely due to stack overflow.\n"); safe_print(STDERR_FILENO, "You might consider increasing the limit with `ulimit -s' or " "equivalent.\n"); } else if (addr < 10 * 1024) { safe_print(STDERR_FILENO, "Looks like a NULL pointer was dereferenced.\n"); } print_statistics(); signal(sig, SIG_DFL); raise(sig); #endif /* CVC5_DEBUG */ } #endif /* HAVE_SIGALTSTACK */ /** Handler for SIGILL (illegal instruction). */ void ill_handler(int sig, siginfo_t* info, void*) { #ifdef CVC5_DEBUG safe_print(STDERR_FILENO, "cvc5 executed an illegal instruction in DEBUG mode.\n"); if (!segvSpin) { print_statistics(); signal(sig, SIG_DFL); raise(sig); } else { safe_print(STDERR_FILENO, "Spinning so that a debugger can be connected.\n"); safe_print(STDERR_FILENO, "Try: gdb "); safe_print(STDERR_FILENO, *progName); safe_print(STDERR_FILENO, " "); safe_print(STDERR_FILENO, getpid()); safe_print(STDERR_FILENO, "\n"); safe_print(STDERR_FILENO, " or: gdb --pid="); safe_print(STDERR_FILENO, getpid()); safe_print(STDERR_FILENO, " "); safe_print(STDERR_FILENO, *progName); safe_print(STDERR_FILENO, "\n"); for (;;) { sleep(60); } } #else /* CVC5_DEBUG */ safe_print(STDERR_FILENO, "cvc5 executed an illegal instruction.\n"); print_statistics(); signal(sig, SIG_DFL); raise(sig); #endif /* CVC5_DEBUG */ } #endif /* __WIN32__ */ static terminate_handler default_terminator; void cvc5terminate() { set_terminate(default_terminator); #ifdef CVC5_DEBUG LastExceptionBuffer* current = LastExceptionBuffer::getCurrent(); LastExceptionBuffer::setCurrent(NULL); delete current; safe_print(STDERR_FILENO, "\n" "cvc5 was terminated by the C++ runtime.\n" "Perhaps an exception was thrown during stack unwinding. " "(Don't do that.)\n"); print_statistics(); default_terminator(); #else /* CVC5_DEBUG */ safe_print(STDERR_FILENO, "cvc5 was terminated by the C++ runtime.\n" "Perhaps an exception was thrown during stack unwinding.\n"); print_statistics(); default_terminator(); #endif /* CVC5_DEBUG */ } void install() { #ifdef CVC5_DEBUG LastExceptionBuffer::setCurrent(new LastExceptionBuffer()); #endif #ifndef __WIN32__ struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit)) { throw Exception(string("getrlimit() failure: ") + strerror(errno)); } if (limit.rlim_cur != limit.rlim_max) { limit.rlim_cur = limit.rlim_max; if (setrlimit(RLIMIT_STACK, &limit)) { throw Exception(string("setrlimit() failure: ") + strerror(errno)); } if (getrlimit(RLIMIT_STACK, &limit)) { throw Exception(string("getrlimit() failure: ") + strerror(errno)); } } struct sigaction act1; act1.sa_sigaction = sigint_handler; act1.sa_flags = SA_SIGINFO; sigemptyset(&act1.sa_mask); if (sigaction(SIGINT, &act1, NULL)) { throw Exception(string("sigaction(SIGINT) failure: ") + strerror(errno)); } struct sigaction act2; act2.sa_sigaction = timeout_handler; act2.sa_flags = SA_SIGINFO; sigemptyset(&act2.sa_mask); if (sigaction(SIGXCPU, &act2, NULL)) { throw Exception(string("sigaction(SIGXCPU) failure: ") + strerror(errno)); } struct sigaction act3; act3.sa_sigaction = ill_handler; act3.sa_flags = SA_SIGINFO; sigemptyset(&act3.sa_mask); if (sigaction(SIGILL, &act3, NULL)) { throw Exception(string("sigaction(SIGILL) failure: ") + strerror(errno)); } #ifdef HAVE_SIGALTSTACK stack_t ss; ss.ss_sp = (char*)malloc(SIGSTKSZ); if (ss.ss_sp == NULL) { throw Exception("Can't malloc() space for a signal stack"); } ss.ss_size = SIGSTKSZ; ss.ss_flags = 0; if (sigaltstack(&ss, NULL) == -1) { throw Exception(string("sigaltstack() failure: ") + strerror(errno)); } stackSize = limit.rlim_cur; stackBase = ss.ss_sp; struct sigaction act4; act4.sa_sigaction = segv_handler; act4.sa_flags = SA_SIGINFO | SA_ONSTACK; sigemptyset(&act4.sa_mask); if (sigaction(SIGSEGV, &act4, NULL)) { throw Exception(string("sigaction(SIGSEGV) failure: ") + strerror(errno)); } #endif /* HAVE_SIGALTSTACK */ struct sigaction act5; act5.sa_sigaction = sigterm_handler; act5.sa_flags = SA_SIGINFO; sigemptyset(&act5.sa_mask); if (sigaction(SIGTERM, &act5, NULL)) { throw Exception(string("sigaction(SIGTERM) failure: ") + strerror(errno)); } #endif /* __WIN32__ */ default_terminator = set_terminate(cvc5terminate); } void cleanup() noexcept { #ifndef __WIN32__ #ifdef HAVE_SIGALTSTACK free(stackBase); stackBase = nullptr; stackSize = 0; #endif /* HAVE_SIGALTSTACK */ #endif /* __WIN32__ */ } } // namespace signal_handlers } // namespace main } // namespace cvc5