Improve stack overflow recovery

It's now possible to use sigaltstack() to recover from stack overflows
on Windows. Several bugs in sigaltstack() have been fixed, for all our
supported platforms. There's a newer better example showing how to use
this, along with three independent unit tests just to further showcase
the various techniques.
This commit is contained in:
Justine Tunney 2023-10-04 07:07:43 -07:00
parent 1694edf85c
commit 4631d34d0d
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
24 changed files with 590 additions and 274 deletions

View file

@ -19,45 +19,19 @@
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/ucontext.internal.h"
#include "libc/calls/ucontext.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/sig.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
/**
* Returns true if signal is likely a stack overflow.
* Returns true if signal is most likely a stack overflow.
*/
char __is_stack_overflow(siginfo_t *si, void *ucontext) {
// check if signal has the information we need
ucontext_t *uc = ucontext;
if (!si) return false;
if (!uc) return false;
char __is_stack_overflow(siginfo_t *si, void *arg) {
ucontext_t *uc = arg;
if (!si || !uc) return false;
if (si->si_signo != SIGSEGV && si->si_signo != SIGBUS) return false;
// with threads we know exactly where the guard page is
int pagesz = getauxval(AT_PAGESZ);
uintptr_t addr = (uintptr_t)si->si_addr;
struct PosixThread *pt = _pthread_self();
if (pt->attr.__stacksize) {
uintptr_t stack = (uintptr_t)pt->attr.__stackaddr;
uintptr_t guard = pt->attr.__guardsize;
uintptr_t bot, top;
if (guard) {
bot = stack;
top = bot + guard;
} else {
bot = stack - pagesz;
top = stack;
}
return addr >= bot && addr < top;
}
// it's easy to guess with the main stack
// even though it's hard to know its exact boundaries
uintptr_t sp = uc->uc_mcontext.SP;
return addr <= sp && addr >= sp - pagesz;
intptr_t sp = uc->uc_mcontext.SP;
intptr_t fp = (intptr_t)si->si_addr;
return ABS(fp - sp) < getauxval(AT_PAGESZ);
}

View file

@ -28,6 +28,7 @@
#include "libc/intrin/bsr.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/likely.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
@ -37,6 +38,9 @@
#include "libc/log/libfatal.internal.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/memflags.h"
#include "libc/nt/enum/pageflags.h"
#include "libc/nt/memory.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/struct/processmemorycounters.h"
@ -396,9 +400,15 @@ inline void *__mmap_unlocked(void *addr, size_t size, int prot, int flags,
kAsanMmapSizeOverrun);
}
if (needguard) {
unassert(!mprotect(p, pagesize, PROT_NONE));
if (IsAsan()) {
__asan_poison(p, pagesize, kAsanStackOverflow);
if (!IsWindows()) {
unassert(!mprotect(p, pagesize, PROT_NONE));
if (IsAsan()) {
__asan_poison(p, pagesize, kAsanStackOverflow);
}
} else {
uint32_t oldattr;
unassert(VirtualProtect(p, pagesize, kNtPageReadwrite | kNtPageGuard,
&oldattr));
}
}
}

View file

@ -169,6 +169,9 @@ static abi wontreturn void WinInit(const char16_t *cmdline) {
uint32_t old;
__imp_VirtualProtect((void *)stackaddr, stacksize, kNtPageReadwrite, &old);
}
uint32_t oldattr;
__imp_VirtualProtect((void *)stackaddr, GetGuardSize(),
kNtPageReadwrite | kNtPageGuard, &oldattr);
_mmi.p[0].x = stackaddr >> 16;
_mmi.p[0].y = (stackaddr >> 16) + ((stacksize - 1) >> 16);
_mmi.p[0].prot = prot;