diff options
Diffstat (limited to 'third_party/nix/src/libmain/stack.cc')
-rw-r--r-- | third_party/nix/src/libmain/stack.cc | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/third_party/nix/src/libmain/stack.cc b/third_party/nix/src/libmain/stack.cc new file mode 100644 index 000000000000..628b6313a89c --- /dev/null +++ b/third_party/nix/src/libmain/stack.cc @@ -0,0 +1,75 @@ +#include <csignal> +#include <cstddef> +#include <cstdlib> +#include <cstring> + +#include <unistd.h> + +#include "libutil/types.hh" + +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 = nullptr; +#if defined(__x86_64__) && defined(REG_RSP) + sp = (char*)(static_cast<ucontext_t*>(ctx))->uc_mcontext.gregs[REG_RSP]; +#elif defined(REG_ESP) + sp = (char*)((ucontext_t*)ctx)->uc_mcontext.gregs[REG_ESP]; +#else + haveSP = false; +#endif + + if (haveSP) { + ptrdiff_t diff = static_cast<char*>(info->si_addr) - sp; + if (diff < 0) { + diff = -diff; + } + if (diff < 4096) { + char msg[] = "error: stack overflow (possible infinite recursion)\n"; + [[gnu::unused]] auto 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, nullptr) != 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; + static auto stackBuf = std::make_unique<std::vector<char>>(stack.ss_size); + stack.ss_sp = stackBuf->data(); + if (stack.ss_sp == nullptr) { + throw Error("cannot allocate alternative stack"); + } + stack.ss_flags = 0; + if (sigaltstack(&stack, nullptr) == -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, nullptr) != 0) { + throw SysError("resetting SIGSEGV"); + } +#endif +} + +} // namespace nix |