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

@ -57,51 +57,36 @@ static void sigaltstack2linux(struct sigaltstack *linux,
static textwindows int sigaltstack_cosmo(const struct sigaltstack *neu,
struct sigaltstack *old) {
struct CosmoTib *tib;
tib = __get_tls();
struct CosmoTib *tib = __get_tls();
char *bp = __builtin_frame_address(0);
if (old) {
old->ss_sp = tib->tib_sigstack_addr;
old->ss_size = tib->tib_sigstack_size;
old->ss_flags = tib->tib_sigstack_flags;
old->ss_flags = tib->tib_sigstack_flags & ~SS_ONSTACK;
}
if (neu) {
tib->tib_sigstack_addr = (char *)ROUNDUP((uintptr_t)neu->ss_sp, 16);
tib->tib_sigstack_size = ROUNDDOWN(neu->ss_size, 16);
tib->tib_sigstack_flags &= SS_ONSTACK;
tib->tib_sigstack_flags |= neu->ss_flags & SS_DISABLE;
tib->tib_sigstack_flags = neu->ss_flags & SS_DISABLE;
}
if (tib->tib_sigstack_addr <= bp &&
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) {
if (old) old->ss_flags |= SS_ONSTACK;
tib->tib_sigstack_flags = SS_ONSTACK; // can't disable if on it
} else if (!tib->tib_sigstack_size) {
if (old) old->ss_flags = SS_DISABLE;
tib->tib_sigstack_flags = SS_DISABLE;
}
return 0;
}
static int sigaltstack_sysv(const struct sigaltstack *neu,
struct sigaltstack *old) {
void *b;
const void *a;
struct sigaltstack_bsd bsd;
if (IsLinux()) {
a = neu;
b = old;
} else {
if (neu) {
sigaltstack2bsd(&bsd, neu);
a = &bsd;
} else {
a = 0;
}
if (old) {
b = &bsd;
} else {
b = 0;
}
}
if (!sys_sigaltstack(a, b)) {
if (IsBsd() && old) {
sigaltstack2linux(old, &bsd);
}
return 0;
} else {
return -1;
}
static int sigaltstack_bsd(const struct sigaltstack *neu,
struct sigaltstack *old) {
struct sigaltstack_bsd oldbsd, neubsd, *neup = 0;
if (neu) sigaltstack2bsd(&neubsd, neu), neup = &neubsd;
if (sys_sigaltstack(neup, &oldbsd) == -1) return -1;
if (old) sigaltstack2linux(old, &oldbsd);
return 0;
}
/**
@ -131,13 +116,13 @@ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
rc = efault();
} else if (neu && ((neu->ss_size >> 32) || //
(neu->ss_flags & ~(SS_ONSTACK | SS_DISABLE)))) {
return einval();
rc = einval();
} else if (neu && neu->ss_size < __get_minsigstksz()) {
rc = enomem();
} else if (IsLinux() || IsBsd()) {
if (!(rc = sigaltstack_sysv(neu, old))) {
sigaltstack_cosmo(neu, old);
}
} else if (IsLinux()) {
rc = sys_sigaltstack(neu, old);
} else if (IsBsd()) {
rc = sigaltstack_bsd(neu, old);
} else {
rc = sigaltstack_cosmo(neu, old);
}