lean4-htt/src/runtime/stack_overflow.cpp
Eric Wieser f2573dc51e
fix: Do not overwrite existing signal handlers (#5062)
Such handlers can come from address sanitizers and similar. When
combined with #4971, this forward-ports
676b9bc477
/ rust-lang/rust#69685

---------

Co-authored-by: Sebastian Ullrich <sebasti@nullri.ch>
2024-08-19 09:11:38 +00:00

123 lines
3.8 KiB
C++

/*
Copyright (c) 2020 Sebastian Ullrich. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Sebastian Ullrich
Print a nicer error message on stack overflow.
Port of the corresponding Rust code (see links below).
*/
#ifdef LEAN_WINDOWS
#include <windows.h>
#else
#include <csignal>
#include <pthread.h>
#include <unistd.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <lean/lean.h>
#include <initializer_list>
#include "runtime/stack_overflow.h"
namespace lean {
// stack guard of the main thread
static stack_guard * g_stack_guard;
#ifdef LEAN_WINDOWS
// https://github.com/rust-lang/rust/blob/master/src/libstd/sys/windows/stack_overflow.rs
LONG WINAPI stack_overflow_handler(struct _EXCEPTION_POINTERS * info) {
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
fprintf(stderr, "\nStack overflow detected. Aborting.\n");
abort();
} else {
return EXCEPTION_CONTINUE_SEARCH;
}
}
stack_guard::stack_guard() {
// reserve some stack space for the handler
ULONG sz = 0x5000;
SetThreadStackGuarantee(&sz);
}
stack_guard::~stack_guard() {}
#else
// Install a segfault signal handler and abort with custom message if address is within stack guard.
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/unix/stack_overflow.rs
// https://github.com/rust-lang/rust/blob/7c8dbd969dd0ef2af6d8bab9e03ba7ce6cac41a2/src/libstd/sys/unix/thread.rs#L293
bool is_within_stack_guard(void * addr) {
char * stackaddr;
#ifdef __APPLE__
stackaddr = static_cast<char *>(pthread_get_stackaddr_np(pthread_self())) - pthread_get_stacksize_np(pthread_self());
#else
pthread_attr_t attr;
if (pthread_attr_init(&attr) != 0) return false;
pthread_getattr_np(pthread_self(), &attr);
size_t stacksize;
pthread_attr_getstack(&attr, reinterpret_cast<void **>(&stackaddr), &stacksize);
pthread_attr_destroy(&attr);
#endif
// close enough; the actual guard might be bigger, but it's unlikely a Lean function will have stack frames that big
size_t guardsize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
// the stack guard is *below* the stack
return stackaddr - guardsize <= addr && addr < stackaddr;
}
extern "C" LEAN_EXPORT void segv_handler(int signum, siginfo_t * info, void *) {
if (is_within_stack_guard(info->si_addr)) {
char const msg[] = "\nStack overflow detected. Aborting.\n";
write(STDERR_FILENO, msg, sizeof(msg) - 1);
abort();
} else {
// reset signal handler and return; see comments in Rust code
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = SIG_DFL;
sigaction(signum, &action, nullptr);
}
}
stack_guard::stack_guard() {
m_signal_stack.ss_sp = malloc(SIGSTKSZ);
if (m_signal_stack.ss_sp == nullptr) return;
m_signal_stack.ss_size = SIGSTKSZ;
m_signal_stack.ss_flags = 0;
sigaltstack(&m_signal_stack, nullptr);
}
stack_guard::~stack_guard() {
if (!m_signal_stack.ss_sp) return;
m_signal_stack.ss_flags = SS_DISABLE;
sigaltstack(&m_signal_stack, nullptr);
free(m_signal_stack.ss_sp);
}
#endif
void initialize_stack_overflow() {
g_stack_guard = new stack_guard();
#ifdef LEAN_WINDOWS
AddVectoredExceptionHandler(0, stack_overflow_handler);
#else
for (auto signum : {SIGSEGV, SIGBUS}) {
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
sigaction(signum, nullptr, &action);
// Configure our signal handler if one is not already set.
if (action.sa_handler == SIG_DFL) {
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = segv_handler;
sigaction(signum, &action, nullptr);
}
}
#endif
}
void finalize_stack_overflow() {
delete g_stack_guard;
}
}