forked from lix-project/lix
73 lines
2 KiB
C++
73 lines
2 KiB
C++
#include "config.h"
|
|
|
|
#include "types.hh"
|
|
|
|
#include <cstring>
|
|
#include <cstddef>
|
|
#include <cstdlib>
|
|
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
|
|
namespace nix {
|
|
|
|
|
|
static void sigsegvHandler(int signo, siginfo_t * info, void * ctx)
|
|
{
|
|
/* Detect stack overflows by comparing the faulting address with
|
|
the stack pointer. Unfortunately, getting the stack pointer is
|
|
not portable. */
|
|
bool haveSP = true;
|
|
char * sp = 0;
|
|
#if defined(__x86_64__) && defined(REG_RSP)
|
|
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_RSP];
|
|
#elif defined(REG_ESP)
|
|
sp = (char *) ((ucontext *) ctx)->uc_mcontext.gregs[REG_ESP];
|
|
#else
|
|
haveSP = false;
|
|
#endif
|
|
|
|
if (haveSP) {
|
|
ptrdiff_t diff = (char *) info->si_addr - sp;
|
|
if (diff < 0) diff = -diff;
|
|
if (diff < 4096) {
|
|
char msg[] = "error: stack overflow (possible infinite recursion)\n";
|
|
[[gnu::unused]] int res = write(2, msg, strlen(msg));
|
|
_exit(1); // maybe abort instead?
|
|
}
|
|
}
|
|
|
|
/* Restore default behaviour (i.e. segfault and dump core). */
|
|
struct sigaction act;
|
|
sigfillset(&act.sa_mask);
|
|
act.sa_handler = SIG_DFL;
|
|
act.sa_flags = 0;
|
|
if (sigaction(SIGSEGV, &act, 0)) abort();
|
|
}
|
|
|
|
|
|
void detectStackOverflow()
|
|
{
|
|
#if defined(SA_SIGINFO) && defined (SA_ONSTACK)
|
|
/* Install a SIGSEGV handler to detect stack overflows. This
|
|
requires an alternative stack, otherwise the signal cannot be
|
|
delivered when we're out of stack space. */
|
|
stack_t stack;
|
|
stack.ss_size = 4096 * 4 + MINSIGSTKSZ;
|
|
stack.ss_sp = new char[stack.ss_size];
|
|
if (!stack.ss_sp) throw Error("cannot allocate alternative stack");
|
|
stack.ss_flags = 0;
|
|
if (sigaltstack(&stack, 0) == -1) throw SysError("cannot set alternative stack");
|
|
|
|
struct sigaction act;
|
|
sigfillset(&act.sa_mask);
|
|
act.sa_sigaction = sigsegvHandler;
|
|
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
|
if (sigaction(SIGSEGV, &act, 0))
|
|
throw SysError("resetting SIGCHLD");
|
|
#endif
|
|
}
|
|
|
|
|
|
}
|