mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
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:
parent
1694edf85c
commit
4631d34d0d
24 changed files with 590 additions and 274 deletions
|
@ -7,74 +7,104 @@
|
||||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
#endif
|
#endif
|
||||||
#include "libc/calls/calls.h"
|
#include "libc/calls/struct/sigaction.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
|
#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/limits.h"
|
#include "libc/limits.h"
|
||||||
#include "libc/log/check.h"
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/runtime/stack.h"
|
#include "libc/runtime/sysconf.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/sysv/consts/sa.h"
|
||||||
#include "libc/sysv/consts/prot.h"
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/sysv/consts/ss.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fileoverview Stack Overflow Demo
|
* stack overflow recovery technique #3
|
||||||
|
* rewrite thread cpu state to call pthread_exit
|
||||||
|
* this method returns gracefully from signal handlers
|
||||||
|
* unfortunately it relies on cpu architecture knowledge
|
||||||
|
*
|
||||||
|
* @see test/libc/thread/stackoverflow1_test.c
|
||||||
|
* @see test/libc/thread/stackoverflow2_test.c
|
||||||
|
* @see test/libc/thread/stackoverflow3_test.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define N INT_MAX
|
volatile bool smashed_stack;
|
||||||
|
|
||||||
int A(int f(), int n) {
|
void Exiter(void *rc) {
|
||||||
if (n < N) {
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(0, ss.ss_flags);
|
||||||
|
pthread_exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CrashHandler(int sig, siginfo_t *si, void *arg) {
|
||||||
|
ucontext_t *ctx = arg;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
||||||
|
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
||||||
|
smashed_stack = true;
|
||||||
|
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
||||||
|
//
|
||||||
|
// the backtrace will look like this
|
||||||
|
//
|
||||||
|
// 0x000000000042561d: pthread_exit at pthread_exit.c:99
|
||||||
|
// 0x0000000000418777: Exiter at stackoverflow2_test.c:40
|
||||||
|
// 0x00000000004186d8: CrashHandler at stackoverflow2_test.c:49
|
||||||
|
// 0x000000000041872a: StackOverflow at stackoverflow2_test.c:53
|
||||||
|
// 0x000000000041872a: StackOverflow at stackoverflow2_test.c:53
|
||||||
|
// 0x000000000041872a: StackOverflow at stackoverflow2_test.c:53
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
ctx->uc_mcontext.ARG0 = 123;
|
||||||
|
ctx->uc_mcontext.PC = (long)Exiter;
|
||||||
|
ctx->uc_mcontext.SP += 32768;
|
||||||
|
ctx->uc_mcontext.SP &= -16;
|
||||||
|
ctx->uc_mcontext.SP -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StackOverflow(int f(), int n) {
|
||||||
|
if (n < INT_MAX) {
|
||||||
return f(f, n + 1) - 1;
|
return f(f, n + 1) - 1;
|
||||||
} else {
|
} else {
|
||||||
return N;
|
return INT_MAX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int (*Ap)(int (*)(), int) = A;
|
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||||
|
|
||||||
|
void *MyPosixThread(void *arg) {
|
||||||
|
struct sigaction sa;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ss.ss_flags = 0;
|
||||||
|
ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096;
|
||||||
|
ss.ss_sp = gc(malloc(ss.ss_size));
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_sigaction = CrashHandler;
|
||||||
|
sigaction(SIGBUS, &sa, 0);
|
||||||
|
sigaction(SIGSEGV, &sa, 0);
|
||||||
|
exit(pStackOverflow(pStackOverflow, 0));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
if (IsWindows()) {
|
void *res;
|
||||||
fprintf(stderr, "stack overflow not possible to catch on windows yet\n");
|
pthread_t th;
|
||||||
exit(1);
|
struct sigaltstack ss;
|
||||||
}
|
smashed_stack = false;
|
||||||
ShowCrashReports();
|
pthread_create(&th, 0, MyPosixThread, 0);
|
||||||
return !!Ap(Ap, 0);
|
pthread_join(th, &res);
|
||||||
|
ASSERT_EQ((void *)123L, res);
|
||||||
|
ASSERT_TRUE(smashed_stack);
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(SS_DISABLE, ss.ss_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
error: Uncaught SIGSEGV (Stack Overflow) on rhel5 pid 368
|
|
||||||
./o//examples/stackoverflow.com
|
|
||||||
EUNKNOWN[No error information][0]
|
|
||||||
Linux rhel5 2.6.18-8.el5 #1 SMP Thu Mar 15 19:46:53 EDT 2007
|
|
||||||
|
|
||||||
0x0000000000406896: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
0x0000000000406898: A at examples/stackoverflow.c:24
|
|
||||||
etc. etc.
|
|
||||||
|
|
||||||
RAX 0000000000000000 RBX 0000000000000001 RDI 000000000040687e ST(0) 0.0
|
|
||||||
RCX 0000000000417125 RDX 000000000041cd70 RSI 0000000000000efe ST(1) 0.0
|
|
||||||
RBP 00006ffffffe1000 RSP 00006ffffffe1000 RIP 0000000000406897 ST(2) 0.0
|
|
||||||
R8 0000000000000000 R9 0000000000000022 R10 0000000000000008 ST(3) 0.0
|
|
||||||
R11 0000000000000293 R12 0000000000000001 R13 00007ffc70b4fc48 ST(4) 0.0
|
|
||||||
R14 00007ffc70b4fc58 R15 00007ffc70b4fd18 VF IF
|
|
||||||
|
|
||||||
XMM0 00000000000000000000000000000000 XMM8 00000000000000000000000000000000
|
|
||||||
XMM1 ffffffffffffeb030000000000000000 XMM9 00000000000000000000000000000000
|
|
||||||
XMM2 0000000000000000ffffffffffffffff XMM10 00000000000000000000000000000000
|
|
||||||
XMM3 00000000000000000000000000000000 XMM11 00000000000000000000000000000000
|
|
||||||
XMM4 00000000000000000000000000000000 XMM12 00000000000000000000000000000000
|
|
||||||
XMM5 00000000000000000000000000000000 XMM13 00000000000000000000000000000000
|
|
||||||
XMM6 00000000000000000000000000000000 XMM14 00000000000000000000000000000000
|
|
||||||
XMM7 00000000000000000000000000000000 XMM15 00000000000000000000000000000000
|
|
||||||
|
|
||||||
100080000000-100080030000 rw-pa-- 3x automap
|
|
||||||
6ffffffe0000-6fffffff0000 rw-paSF 1x stack
|
|
||||||
# 4 frames mapped w/ 0 frames gapped
|
|
||||||
*/
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ static int FindPromise(const char *name) {
|
||||||
*
|
*
|
||||||
* @return 0 on success, or -1 if invalid
|
* @return 0 on success, or -1 if invalid
|
||||||
*/
|
*/
|
||||||
int ParsePromises(const char *promises, unsigned long *out) {
|
int ParsePromises(const char *promises, unsigned long *out,
|
||||||
|
unsigned long current) {
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int promise;
|
int promise;
|
||||||
unsigned long ipromises;
|
unsigned long ipromises;
|
||||||
|
@ -57,7 +58,7 @@ int ParsePromises(const char *promises, unsigned long *out) {
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ipromises = 0;
|
ipromises = current;
|
||||||
}
|
}
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*out = ipromises;
|
*out = ipromises;
|
||||||
|
|
|
@ -239,9 +239,6 @@
|
||||||
int pledge(const char *promises, const char *execpromises) {
|
int pledge(const char *promises, const char *execpromises) {
|
||||||
int e, rc;
|
int e, rc;
|
||||||
unsigned long ipromises, iexecpromises;
|
unsigned long ipromises, iexecpromises;
|
||||||
if (promises && !execpromises) {
|
|
||||||
execpromises = promises;
|
|
||||||
}
|
|
||||||
if (!promises) {
|
if (!promises) {
|
||||||
// OpenBSD says NULL argument means it doesn't change, i.e.
|
// OpenBSD says NULL argument means it doesn't change, i.e.
|
||||||
// pledge(0,0) on OpenBSD does nothing. The Cosmopolitan Libc
|
// pledge(0,0) on OpenBSD does nothing. The Cosmopolitan Libc
|
||||||
|
@ -262,8 +259,8 @@ int pledge(const char *promises, const char *execpromises) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (!IsTiny() && IsGenuineBlink()) {
|
} else if (!IsTiny() && IsGenuineBlink()) {
|
||||||
rc = 0; // blink doesn't support seccomp; avoid noisy log warnings
|
rc = 0; // blink doesn't support seccomp; avoid noisy log warnings
|
||||||
} else if (!ParsePromises(promises, &ipromises) &&
|
} else if (!ParsePromises(promises, &ipromises, __promises) &&
|
||||||
!ParsePromises(execpromises, &iexecpromises)) {
|
!ParsePromises(execpromises, &iexecpromises, __execpromises)) {
|
||||||
if (IsLinux()) {
|
if (IsLinux()) {
|
||||||
// copy exec and execnative from promises to execpromises
|
// copy exec and execnative from promises to execpromises
|
||||||
iexecpromises = ~(~iexecpromises | (~ipromises & (1ul << PROMISE_EXEC)));
|
iexecpromises = ~(~iexecpromises | (~ipromises & (1ul << PROMISE_EXEC)));
|
||||||
|
|
|
@ -14,7 +14,7 @@ struct Pledges {
|
||||||
extern const struct Pledges kPledge[PROMISE_LEN_];
|
extern const struct Pledges kPledge[PROMISE_LEN_];
|
||||||
|
|
||||||
int sys_pledge_linux(unsigned long, int);
|
int sys_pledge_linux(unsigned long, int);
|
||||||
int ParsePromises(const char *, unsigned long *);
|
int ParsePromises(const char *, unsigned long *, unsigned long);
|
||||||
|
|
||||||
COSMOPOLITAN_C_END_
|
COSMOPOLITAN_C_END_
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
133
libc/calls/sig.c
133
libc/calls/sig.c
|
@ -34,6 +34,7 @@
|
||||||
#include "libc/fmt/itoa.h"
|
#include "libc/fmt/itoa.h"
|
||||||
#include "libc/intrin/atomic.h"
|
#include "libc/intrin/atomic.h"
|
||||||
#include "libc/intrin/describebacktrace.internal.h"
|
#include "libc/intrin/describebacktrace.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/popcnt.h"
|
#include "libc/intrin/popcnt.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
|
@ -72,9 +73,6 @@ struct ContextFrame {
|
||||||
struct NtContext nc;
|
struct NtContext nc;
|
||||||
};
|
};
|
||||||
|
|
||||||
void __stack_call(int, siginfo_t *, void *, long,
|
|
||||||
void (*)(int, siginfo_t *, void *), void *);
|
|
||||||
|
|
||||||
static textwindows bool __sig_ignored_by_default(int sig) {
|
static textwindows bool __sig_ignored_by_default(int sig) {
|
||||||
return sig == SIGURG || //
|
return sig == SIGURG || //
|
||||||
sig == SIGCONT || //
|
sig == SIGCONT || //
|
||||||
|
@ -90,9 +88,21 @@ textwindows bool __sig_ignored(int sig) {
|
||||||
|
|
||||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||||
struct CosmoTib *tib) {
|
struct CosmoTib *tib) {
|
||||||
return (flags & SA_ONSTACK) && //
|
if (!(flags & SA_ONSTACK)) {
|
||||||
tib->tib_sigstack_size && //
|
return false; // signal handler didn't enable it
|
||||||
!(tib->tib_sigstack_flags & (SS_DISABLE | SS_ONSTACK));
|
}
|
||||||
|
if (!tib->tib_sigstack_size) {
|
||||||
|
return false; // sigaltstack() wasn't installed on this thread
|
||||||
|
}
|
||||||
|
if (tib->tib_sigstack_flags & SS_DISABLE) {
|
||||||
|
return false; // sigaltstack() on this thread was disabled by user
|
||||||
|
}
|
||||||
|
char *bp = __builtin_frame_address(0);
|
||||||
|
if (tib->tib_sigstack_addr <= bp &&
|
||||||
|
bp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) {
|
||||||
|
return false; // we're already on the alternate stack
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows wontreturn void __sig_terminate(int sig) {
|
static textwindows wontreturn void __sig_terminate(int sig) {
|
||||||
|
@ -129,20 +139,6 @@ static textwindows sigaction_f __sig_handler(unsigned rva) {
|
||||||
return (sigaction_f)(__executable_start + rva);
|
return (sigaction_f)(__executable_start + rva);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx,
|
|
||||||
unsigned rva, unsigned flags,
|
|
||||||
struct CosmoTib *tib) {
|
|
||||||
++__sig.count;
|
|
||||||
if (__sig_should_use_altstack(flags, tib)) {
|
|
||||||
tib->tib_sigstack_flags |= SS_ONSTACK;
|
|
||||||
__stack_call(sig, si, ctx, 0, __sig_handler(rva),
|
|
||||||
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
|
||||||
tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
|
||||||
} else {
|
|
||||||
__sig_handler(rva)(sig, si, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textwindows int __sig_raise(int sig, int sic) {
|
textwindows int __sig_raise(int sig, int sic) {
|
||||||
unsigned rva, flags;
|
unsigned rva, flags;
|
||||||
struct CosmoTib *tib = __get_tls();
|
struct CosmoTib *tib = __get_tls();
|
||||||
|
@ -160,7 +156,7 @@ textwindows int __sig_raise(int sig, int sic) {
|
||||||
STRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
STRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
||||||
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||||
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}));
|
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}));
|
||||||
__sig_call(sig, &si, &ctx, rva, flags, tib);
|
__sig_handler(rva)(sig, &si, &ctx);
|
||||||
STRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
STRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
||||||
__sig_handler(rva),
|
__sig_handler(rva),
|
||||||
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}),
|
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}),
|
||||||
|
@ -258,9 +254,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
||||||
}
|
}
|
||||||
uintptr_t sp;
|
uintptr_t sp;
|
||||||
if (__sig_should_use_altstack(flags, pt->tib)) {
|
if (__sig_should_use_altstack(flags, pt->tib)) {
|
||||||
pt->tib->tib_sigstack_flags |= SS_ONSTACK;
|
|
||||||
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
|
sp = (uintptr_t)pt->tib->tib_sigstack_addr + pt->tib->tib_sigstack_size;
|
||||||
pt->tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
|
||||||
} else {
|
} else {
|
||||||
sp = (nc.Rsp - 128 - sizeof(struct ContextFrame)) & -16;
|
sp = (nc.Rsp - 128 - sizeof(struct ContextFrame)) & -16;
|
||||||
}
|
}
|
||||||
|
@ -338,11 +332,11 @@ static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
|
||||||
case kNtSignalPrivInstruction:
|
case kNtSignalPrivInstruction:
|
||||||
*code = ILL_PRVOPC;
|
*code = ILL_PRVOPC;
|
||||||
return SIGILL;
|
return SIGILL;
|
||||||
case kNtSignalGuardPage:
|
|
||||||
case kNtSignalInPageError:
|
case kNtSignalInPageError:
|
||||||
case kNtStatusStackOverflow:
|
case kNtStatusStackOverflow:
|
||||||
*code = SEGV_MAPERR;
|
*code = SEGV_MAPERR;
|
||||||
return SIGSEGV;
|
return SIGSEGV;
|
||||||
|
case kNtSignalGuardPage:
|
||||||
case kNtSignalAccessViolation:
|
case kNtSignalAccessViolation:
|
||||||
*code = SEGV_ACCERR;
|
*code = SEGV_ACCERR;
|
||||||
return SIGSEGV;
|
return SIGSEGV;
|
||||||
|
@ -386,26 +380,25 @@ static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// abashed the devil stood, and felt how awful goodness is
|
static void __sig_crasher(struct NtExceptionPointers *ep, int code, int sig,
|
||||||
__msabi unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
struct CosmoTib *tib) {
|
||||||
int code, sig = __sig_crash_sig(ep, &code);
|
|
||||||
|
// increment the signal count for getrusage()
|
||||||
|
++__sig.count;
|
||||||
|
|
||||||
|
// log vital crash information reliably for --strace before doing much
|
||||||
|
// we don't print this without the flag since raw numbers scare people
|
||||||
|
// this needs at least one page of stack memory in order to get logged
|
||||||
|
// otherwise it'll print a warning message about the lack of stack mem
|
||||||
STRACE("win32 vectored exception 0x%08Xu raising %G "
|
STRACE("win32 vectored exception 0x%08Xu raising %G "
|
||||||
"cosmoaddr2line %s %lx %s",
|
"cosmoaddr2line %s %lx %s",
|
||||||
ep->ExceptionRecord->ExceptionCode, sig, program_invocation_name,
|
ep->ExceptionRecord->ExceptionCode, sig, program_invocation_name,
|
||||||
ep->ContextRecord->Rip,
|
ep->ContextRecord->Rip,
|
||||||
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
|
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
|
||||||
if (sig == SIGTRAP) {
|
|
||||||
ep->ContextRecord->Rip++;
|
// if the user didn't install a signal handler for this unmaskable
|
||||||
if (__sig_ignored(sig)) {
|
// exception, then print a friendly helpful hint message to stderr
|
||||||
return kNtExceptionContinueExecution;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct PosixThread *pt = _pthread_self();
|
|
||||||
siginfo_t si = {.si_signo = sig,
|
|
||||||
.si_code = code,
|
|
||||||
.si_addr = ep->ExceptionRecord->ExceptionAddress};
|
|
||||||
unsigned rva = __sighandrvas[sig];
|
unsigned rva = __sighandrvas[sig];
|
||||||
unsigned flags = __sighandflags[sig];
|
|
||||||
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) {
|
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) {
|
||||||
#ifndef TINY
|
#ifndef TINY
|
||||||
intptr_t hStderr;
|
intptr_t hStderr;
|
||||||
|
@ -418,16 +411,74 @@ __msabi unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
||||||
#endif
|
#endif
|
||||||
__sig_terminate(sig);
|
__sig_terminate(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if this signal handler is configured to auto-reset to the default
|
||||||
|
// then that reset needs to happen before the user handler is called
|
||||||
|
unsigned flags = __sighandflags[sig];
|
||||||
if (flags & SA_RESETHAND) {
|
if (flags & SA_RESETHAND) {
|
||||||
STRACE("resetting %G handler", sig);
|
STRACE("resetting %G handler", sig);
|
||||||
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determine the true memory address at which fault occurred
|
||||||
|
// if this is a stack overflow then reapply guard protection
|
||||||
|
void *si_addr;
|
||||||
|
if (ep->ExceptionRecord->ExceptionCode == kNtSignalGuardPage) {
|
||||||
|
si_addr = (void *)ep->ExceptionRecord->ExceptionInformation[1];
|
||||||
|
} else {
|
||||||
|
si_addr = ep->ExceptionRecord->ExceptionAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// call the user signal handler
|
||||||
|
// with a temporarily replaced signal mask
|
||||||
|
// and a modifiable view of the faulting code's cpu state
|
||||||
|
// note ucontext_t is a hefty data structures on top of NtContext
|
||||||
ucontext_t ctx = {0};
|
ucontext_t ctx = {0};
|
||||||
|
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
|
||||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||||
ctx.uc_sigmask.__bits[0] = pt->tib->tib_sigmask;
|
ctx.uc_sigmask.__bits[0] = tib->tib_sigmask;
|
||||||
__sig_call(sig, &si, &ctx, rva, flags, pt->tib);
|
__sig_handler(rva)(sig, &si, &ctx);
|
||||||
pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||||
_ntlinux2context(ep->ContextRecord, &ctx);
|
_ntlinux2context(ep->ContextRecord, &ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __stack_call(struct NtExceptionPointers *, int, int, struct CosmoTib *,
|
||||||
|
void (*)(struct NtExceptionPointers *, int, int,
|
||||||
|
struct CosmoTib *),
|
||||||
|
void *);
|
||||||
|
|
||||||
|
//
|
||||||
|
// abashed the devil stood
|
||||||
|
// and felt how awful goodness is
|
||||||
|
//
|
||||||
|
__msabi dontinstrument unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
||||||
|
|
||||||
|
// translate win32 to unix si_signo and si_code
|
||||||
|
int code, sig = __sig_crash_sig(ep, &code);
|
||||||
|
|
||||||
|
// advance the instruction pointer to skip over debugger breakpoints
|
||||||
|
// this behavior is consistent with how unix kernels are implemented
|
||||||
|
if (sig == SIGTRAP) {
|
||||||
|
ep->ContextRecord->Rip++;
|
||||||
|
if (__sig_ignored(sig)) {
|
||||||
|
return kNtExceptionContinueExecution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// win32 stack overflow detection executes INSIDE the guard page
|
||||||
|
// thus switch to the alternate signal stack as soon as possible
|
||||||
|
struct CosmoTib *tib = __get_tls();
|
||||||
|
unsigned flags = __sighandflags[sig];
|
||||||
|
if (__sig_should_use_altstack(flags, tib)) {
|
||||||
|
__stack_call(ep, code, sig, tib, __sig_crasher,
|
||||||
|
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
||||||
|
} else {
|
||||||
|
__sig_crasher(ep, code, sig, tib);
|
||||||
|
}
|
||||||
|
|
||||||
|
// resume running user program
|
||||||
|
// hopefully the user fixed the cpu state
|
||||||
|
// otherwise the crash will keep happening
|
||||||
return kNtExceptionContinueExecution;
|
return kNtExceptionContinueExecution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,51 +57,36 @@ static void sigaltstack2linux(struct sigaltstack *linux,
|
||||||
|
|
||||||
static textwindows int sigaltstack_cosmo(const struct sigaltstack *neu,
|
static textwindows int sigaltstack_cosmo(const struct sigaltstack *neu,
|
||||||
struct sigaltstack *old) {
|
struct sigaltstack *old) {
|
||||||
struct CosmoTib *tib;
|
struct CosmoTib *tib = __get_tls();
|
||||||
tib = __get_tls();
|
char *bp = __builtin_frame_address(0);
|
||||||
if (old) {
|
if (old) {
|
||||||
old->ss_sp = tib->tib_sigstack_addr;
|
old->ss_sp = tib->tib_sigstack_addr;
|
||||||
old->ss_size = tib->tib_sigstack_size;
|
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) {
|
if (neu) {
|
||||||
tib->tib_sigstack_addr = (char *)ROUNDUP((uintptr_t)neu->ss_sp, 16);
|
tib->tib_sigstack_addr = (char *)ROUNDUP((uintptr_t)neu->ss_sp, 16);
|
||||||
tib->tib_sigstack_size = ROUNDDOWN(neu->ss_size, 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sigaltstack_sysv(const struct sigaltstack *neu,
|
static int sigaltstack_bsd(const struct sigaltstack *neu,
|
||||||
struct sigaltstack *old) {
|
struct sigaltstack *old) {
|
||||||
void *b;
|
struct sigaltstack_bsd oldbsd, neubsd, *neup = 0;
|
||||||
const void *a;
|
if (neu) sigaltstack2bsd(&neubsd, neu), neup = &neubsd;
|
||||||
struct sigaltstack_bsd bsd;
|
if (sys_sigaltstack(neup, &oldbsd) == -1) return -1;
|
||||||
if (IsLinux()) {
|
if (old) sigaltstack2linux(old, &oldbsd);
|
||||||
a = neu;
|
return 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -131,13 +116,13 @@ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
|
||||||
rc = efault();
|
rc = efault();
|
||||||
} else if (neu && ((neu->ss_size >> 32) || //
|
} else if (neu && ((neu->ss_size >> 32) || //
|
||||||
(neu->ss_flags & ~(SS_ONSTACK | SS_DISABLE)))) {
|
(neu->ss_flags & ~(SS_ONSTACK | SS_DISABLE)))) {
|
||||||
return einval();
|
rc = einval();
|
||||||
} else if (neu && neu->ss_size < __get_minsigstksz()) {
|
} else if (neu && neu->ss_size < __get_minsigstksz()) {
|
||||||
rc = enomem();
|
rc = enomem();
|
||||||
} else if (IsLinux() || IsBsd()) {
|
} else if (IsLinux()) {
|
||||||
if (!(rc = sigaltstack_sysv(neu, old))) {
|
rc = sys_sigaltstack(neu, old);
|
||||||
sigaltstack_cosmo(neu, old);
|
} else if (IsBsd()) {
|
||||||
}
|
rc = sigaltstack_bsd(neu, old);
|
||||||
} else {
|
} else {
|
||||||
rc = sigaltstack_cosmo(neu, old);
|
rc = sigaltstack_cosmo(neu, old);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,19 @@
|
||||||
|
|
||||||
dontinstrument const char *(DescribeBacktrace)(char buf[N],
|
dontinstrument const char *(DescribeBacktrace)(char buf[N],
|
||||||
struct StackFrame *fr) {
|
struct StackFrame *fr) {
|
||||||
bool gotsome = false;
|
|
||||||
char *p = buf;
|
char *p = buf;
|
||||||
char *pe = p + N;
|
char *pe = p + N;
|
||||||
|
bool gotsome = false;
|
||||||
while (fr) {
|
while (fr) {
|
||||||
if (gotsome) {
|
if (p + 16 + 1 + 1 <= pe) {
|
||||||
if (p + 1 < pe) {
|
if (gotsome) {
|
||||||
*p++ = ' ';
|
*p++ = ' ';
|
||||||
*p = 0;
|
} else {
|
||||||
|
gotsome = true;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
gotsome = true;
|
|
||||||
}
|
|
||||||
if (p + 17 <= pe) {
|
|
||||||
p = __hexcpy(p, fr->addr);
|
p = __hexcpy(p, fr->addr);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
fr = fr->next;
|
fr = fr->next;
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ void __get_main_stack(void **out_addr, size_t *out_size, int *out_guardsize) {
|
||||||
if (IsWindows()) {
|
if (IsWindows()) {
|
||||||
*out_addr = (void *)GetStaticStackAddr(0);
|
*out_addr = (void *)GetStaticStackAddr(0);
|
||||||
*out_size = GetStaticStackSize();
|
*out_size = GetStaticStackSize();
|
||||||
*out_guardsize = 4096;
|
*out_guardsize = GetGuardSize();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int pagesz = getauxval(AT_PAGESZ);
|
int pagesz = getauxval(AT_PAGESZ);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include "libc/intrin/likely.h"
|
#include "libc/intrin/likely.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
|
||||||
#define _NTTRACE 1 /* not configurable w/ flag yet */
|
#define _NTTRACE 0 /* not configurable w/ flag yet */
|
||||||
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
#define _POLLTRACE 0 /* not configurable w/ flag yet */
|
||||||
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
#define _DATATRACE 1 /* not configurable w/ flag yet */
|
||||||
#define _LOCKTRACE 0 /* not configurable w/ flag yet */
|
#define _LOCKTRACE 0 /* not configurable w/ flag yet */
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
|
||||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
|
||||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
||||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
|
||||||
│ │
|
|
||||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
|
||||||
│ any purpose with or without fee is hereby granted, provided that the │
|
|
||||||
│ above copyright notice and this permission notice appear in all copies. │
|
|
||||||
│ │
|
|
||||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
|
||||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
|
||||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
|
||||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
|
||||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
|
||||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
|
||||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
|
||||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
||||||
#include "libc/log/internal.h"
|
|
||||||
|
|
||||||
bool _wantcrashreports;
|
|
|
@ -5,7 +5,6 @@
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
extern bool __nocolor;
|
extern bool __nocolor;
|
||||||
extern bool _wantcrashreports;
|
|
||||||
extern bool g_isrunningundermake;
|
extern bool g_isrunningundermake;
|
||||||
|
|
||||||
void __start_fatal(const char *, int);
|
void __start_fatal(const char *, int);
|
||||||
|
|
|
@ -91,7 +91,6 @@ void ShowCrashReports(void) {
|
||||||
InstallCrashHandler(SIGBUS, SA_RESETHAND);
|
InstallCrashHandler(SIGBUS, SA_RESETHAND);
|
||||||
InstallCrashHandler(SIGABRT, SA_RESETHAND);
|
InstallCrashHandler(SIGABRT, SA_RESETHAND);
|
||||||
InstallCrashHandler(SIGSEGV, SA_RESETHAND | SA_ONSTACK);
|
InstallCrashHandler(SIGSEGV, SA_RESETHAND | SA_ONSTACK);
|
||||||
_wantcrashreports = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IGNORE_LEAKS(ShowCrashReports)
|
IGNORE_LEAKS(ShowCrashReports)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#define COSMOPOLITAN_LIBC_NT_STRUCT_NTEXCEPTIONRECORD_H_
|
#define COSMOPOLITAN_LIBC_NT_STRUCT_NTEXCEPTIONRECORD_H_
|
||||||
|
|
||||||
#define kNtExceptionMaximumParameters 15
|
#define kNtExceptionMaximumParameters 15
|
||||||
#define kNtExceptionNoncontinuable 1
|
#define kNtExceptionNoncontinuable 1
|
||||||
|
|
||||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ struct NtExceptionRecord {
|
||||||
struct NtExceptionRecord *ExceptionRecord; /* nested exceptions */
|
struct NtExceptionRecord *ExceptionRecord; /* nested exceptions */
|
||||||
void *ExceptionAddress; /* %rip */
|
void *ExceptionAddress; /* %rip */
|
||||||
uint32_t NumberParameters; /* #ExceptionInformation */
|
uint32_t NumberParameters; /* #ExceptionInformation */
|
||||||
uint32_t *ExceptionInformation[kNtExceptionMaximumParameters];
|
uint64_t ExceptionInformation[kNtExceptionMaximumParameters];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||||
|
|
|
@ -19,45 +19,19 @@
|
||||||
#include "libc/calls/struct/siginfo.h"
|
#include "libc/calls/struct/siginfo.h"
|
||||||
#include "libc/calls/struct/ucontext.internal.h"
|
#include "libc/calls/struct/ucontext.internal.h"
|
||||||
#include "libc/calls/ucontext.h"
|
#include "libc/calls/ucontext.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/macros.internal.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/sysv/consts/auxv.h"
|
#include "libc/sysv/consts/auxv.h"
|
||||||
#include "libc/sysv/consts/sig.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) {
|
char __is_stack_overflow(siginfo_t *si, void *arg) {
|
||||||
|
ucontext_t *uc = arg;
|
||||||
// check if signal has the information we need
|
if (!si || !uc) return false;
|
||||||
ucontext_t *uc = ucontext;
|
|
||||||
if (!si) return false;
|
|
||||||
if (!uc) return false;
|
|
||||||
if (si->si_signo != SIGSEGV && si->si_signo != SIGBUS) return false;
|
if (si->si_signo != SIGSEGV && si->si_signo != SIGBUS) return false;
|
||||||
|
intptr_t sp = uc->uc_mcontext.SP;
|
||||||
// with threads we know exactly where the guard page is
|
intptr_t fp = (intptr_t)si->si_addr;
|
||||||
int pagesz = getauxval(AT_PAGESZ);
|
return ABS(fp - sp) < 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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "libc/intrin/bsr.h"
|
#include "libc/intrin/bsr.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/directmap.internal.h"
|
#include "libc/intrin/directmap.internal.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/likely.h"
|
#include "libc/intrin/likely.h"
|
||||||
#include "libc/intrin/safemacros.internal.h"
|
#include "libc/intrin/safemacros.internal.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
|
@ -37,6 +38,9 @@
|
||||||
#include "libc/log/libfatal.internal.h"
|
#include "libc/log/libfatal.internal.h"
|
||||||
#include "libc/log/log.h"
|
#include "libc/log/log.h"
|
||||||
#include "libc/macros.internal.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/process.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/struct/processmemorycounters.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);
|
kAsanMmapSizeOverrun);
|
||||||
}
|
}
|
||||||
if (needguard) {
|
if (needguard) {
|
||||||
unassert(!mprotect(p, pagesize, PROT_NONE));
|
if (!IsWindows()) {
|
||||||
if (IsAsan()) {
|
unassert(!mprotect(p, pagesize, PROT_NONE));
|
||||||
__asan_poison(p, pagesize, kAsanStackOverflow);
|
if (IsAsan()) {
|
||||||
|
__asan_poison(p, pagesize, kAsanStackOverflow);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t oldattr;
|
||||||
|
unassert(VirtualProtect(p, pagesize, kNtPageReadwrite | kNtPageGuard,
|
||||||
|
&oldattr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,6 +169,9 @@ static abi wontreturn void WinInit(const char16_t *cmdline) {
|
||||||
uint32_t old;
|
uint32_t old;
|
||||||
__imp_VirtualProtect((void *)stackaddr, stacksize, kNtPageReadwrite, &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].x = stackaddr >> 16;
|
||||||
_mmi.p[0].y = (stackaddr >> 16) + ((stacksize - 1) >> 16);
|
_mmi.p[0].y = (stackaddr >> 16) + ((stacksize - 1) >> 16);
|
||||||
_mmi.p[0].prot = prot;
|
_mmi.p[0].prot = prot;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "libc/intrin/bsr.h"
|
#include "libc/intrin/bsr.h"
|
||||||
#include "libc/intrin/describeflags.internal.h"
|
#include "libc/intrin/describeflags.internal.h"
|
||||||
#include "libc/intrin/dll.h"
|
#include "libc/intrin/dll.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/strace.internal.h"
|
#include "libc/intrin/strace.internal.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/log/internal.h"
|
#include "libc/log/internal.h"
|
||||||
|
@ -38,6 +39,9 @@
|
||||||
#include "libc/mem/alloca.h"
|
#include "libc/mem/alloca.h"
|
||||||
#include "libc/mem/mem.h"
|
#include "libc/mem/mem.h"
|
||||||
#include "libc/nexgen32e/crc32.h"
|
#include "libc/nexgen32e/crc32.h"
|
||||||
|
#include "libc/nt/enum/memflags.h"
|
||||||
|
#include "libc/nt/enum/pageflags.h"
|
||||||
|
#include "libc/nt/memory.h"
|
||||||
#include "libc/nt/runtime.h"
|
#include "libc/nt/runtime.h"
|
||||||
#include "libc/nt/synchronization.h"
|
#include "libc/nt/synchronization.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
|
@ -210,9 +214,19 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
-1, 0, 0) != pt->attr.__stackaddr) {
|
-1, 0, 0) != pt->attr.__stackaddr) {
|
||||||
notpossible;
|
notpossible;
|
||||||
}
|
}
|
||||||
if (pt->attr.__guardsize && !IsWindows() &&
|
if (pt->attr.__guardsize) {
|
||||||
mprotect(pt->attr.__stackaddr, pt->attr.__guardsize, PROT_NONE)) {
|
if (!IsWindows()) {
|
||||||
notpossible;
|
if (mprotect(pt->attr.__stackaddr, pt->attr.__guardsize,
|
||||||
|
PROT_NONE)) {
|
||||||
|
notpossible;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint32_t oldattr;
|
||||||
|
if (!VirtualProtect(pt->attr.__stackaddr, pt->attr.__guardsize,
|
||||||
|
kNtPageReadwrite | kNtPageGuard, &oldattr)) {
|
||||||
|
notpossible;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +241,7 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pt->pt_flags |= PT_OWNSTACK;
|
pt->pt_flags |= PT_OWNSTACK;
|
||||||
if (IsAsan() && pt->attr.__guardsize) {
|
if (IsAsan() && !IsWindows() && pt->attr.__guardsize) {
|
||||||
__asan_poison(pt->attr.__stackaddr, pt->attr.__guardsize,
|
__asan_poison(pt->attr.__stackaddr, pt->attr.__guardsize,
|
||||||
kAsanStackOverflow);
|
kAsanStackOverflow);
|
||||||
}
|
}
|
||||||
|
|
|
@ -552,6 +552,8 @@ TEST(pledge_openbsd, execpromisesIsNull_letsItDoAnything) {
|
||||||
}
|
}
|
||||||
EXPECT_NE(-1, wait(&ws));
|
EXPECT_NE(-1, wait(&ws));
|
||||||
EXPECT_TRUE(WIFEXITED(ws));
|
EXPECT_TRUE(WIFEXITED(ws));
|
||||||
|
EXPECT_FALSE(WIFSIGNALED(ws));
|
||||||
|
EXPECT_EQ(0, WTERMSIG(ws));
|
||||||
EXPECT_EQ(3, WEXITSTATUS(ws));
|
EXPECT_EQ(3, WEXITSTATUS(ws));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -255,58 +255,6 @@ TEST(pthread_cleanup, pthread_normal) {
|
||||||
ASSERT_TRUE(g_cleanup2);
|
ASSERT_TRUE(g_cleanup2);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
jmp_buf recover;
|
|
||||||
volatile bool smashed_stack;
|
|
||||||
|
|
||||||
void CrashHandler(int sig, siginfo_t *si, void *ctx) {
|
|
||||||
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
|
||||||
smashed_stack = true;
|
|
||||||
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
|
||||||
longjmp(recover, 123);
|
|
||||||
}
|
|
||||||
|
|
||||||
int StackOverflow(int f(), int n) {
|
|
||||||
if (n < INT_MAX) {
|
|
||||||
return f(f, n + 1) - 1;
|
|
||||||
} else {
|
|
||||||
return INT_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
|
||||||
|
|
||||||
void *MyPosixThread(void *arg) {
|
|
||||||
int jumpcode;
|
|
||||||
struct sigaction sa, o1, o2;
|
|
||||||
struct sigaltstack ss;
|
|
||||||
ss.ss_flags = 0;
|
|
||||||
ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096;
|
|
||||||
ss.ss_sp = gc(malloc(ss.ss_size));
|
|
||||||
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
|
|
||||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sa.sa_sigaction = CrashHandler;
|
|
||||||
sigaction(SIGBUS, &sa, &o1);
|
|
||||||
sigaction(SIGSEGV, &sa, &o2);
|
|
||||||
if (!(jumpcode = setjmp(recover))) {
|
|
||||||
exit(pStackOverflow(pStackOverflow, 0));
|
|
||||||
}
|
|
||||||
ASSERT_EQ(123, jumpcode);
|
|
||||||
sigaction(SIGSEGV, &o2, 0);
|
|
||||||
sigaction(SIGBUS, &o1, 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cosmo, altstack_thread) {
|
|
||||||
pthread_t th;
|
|
||||||
if (IsWindows()) return;
|
|
||||||
pthread_create(&th, 0, MyPosixThread, 0);
|
|
||||||
pthread_join(th, 0);
|
|
||||||
ASSERT_TRUE(smashed_stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// BENCHMARKS
|
// BENCHMARKS
|
||||||
|
|
||||||
|
|
104
test/libc/thread/stackoverflow1_test.c
Normal file
104
test/libc/thread/stackoverflow1_test.c
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/struct/rlimit.h"
|
||||||
|
#include "libc/calls/struct/sigaction.h"
|
||||||
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
|
#include "libc/calls/struct/siginfo.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/runtime/sysconf.h"
|
||||||
|
#include "libc/sysv/consts/rlimit.h"
|
||||||
|
#include "libc/sysv/consts/sa.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/sysv/consts/ss.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stack overflow recovery technique #1
|
||||||
|
* overflow the gigantic main process stack
|
||||||
|
* simple but it can upset kernels / libraries
|
||||||
|
*/
|
||||||
|
|
||||||
|
jmp_buf recover;
|
||||||
|
volatile bool smashed_stack;
|
||||||
|
|
||||||
|
void CrashHandler(int sig, siginfo_t *si, void *ctx) {
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
||||||
|
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
||||||
|
smashed_stack = true;
|
||||||
|
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
||||||
|
longjmp(recover, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp(void) {
|
||||||
|
|
||||||
|
// tune down the main process's stack size to a reasonable amount
|
||||||
|
// some operating systems, e.g. freebsd, will do things like have
|
||||||
|
// 500mb RLIMIT_STACK by default, even on machines with 400mb RAM
|
||||||
|
struct rlimit rl = {2 * 1024 * 1024, 2 * 1024 * 1024};
|
||||||
|
if (!IsWindows()) setrlimit(RLIMIT_STACK, &rl);
|
||||||
|
|
||||||
|
// set up the signal handler and alternative stack
|
||||||
|
struct sigaction sa;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ss.ss_flags = 0;
|
||||||
|
ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192;
|
||||||
|
ss.ss_sp = _mapanon(ss.ss_size);
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_sigaction = CrashHandler;
|
||||||
|
sigaction(SIGBUS, &sa, 0);
|
||||||
|
sigaction(SIGSEGV, &sa, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StackOverflow(int f(), int n) {
|
||||||
|
if (n < INT_MAX) {
|
||||||
|
return f(f, n + 1) - 1;
|
||||||
|
} else {
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||||
|
|
||||||
|
TEST(stackoverflow, standardStack_altStack_process_longjmp) {
|
||||||
|
int jumpcode;
|
||||||
|
if (!(jumpcode = setjmp(recover))) {
|
||||||
|
exit(pStackOverflow(pStackOverflow, 0));
|
||||||
|
}
|
||||||
|
ASSERT_EQ(123, jumpcode);
|
||||||
|
ASSERT_TRUE(smashed_stack);
|
||||||
|
|
||||||
|
// here's where longjmp() gets us into trouble
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
if (IsXnu() || IsNetbsd()) {
|
||||||
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags); // wut
|
||||||
|
} else {
|
||||||
|
ASSERT_EQ(0, ss.ss_flags);
|
||||||
|
}
|
||||||
|
}
|
105
test/libc/thread/stackoverflow2_test.c
Normal file
105
test/libc/thread/stackoverflow2_test.c
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/struct/sigaction.h"
|
||||||
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
|
#include "libc/calls/struct/siginfo.h"
|
||||||
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
|
#include "libc/limits.h"
|
||||||
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/runtime/sysconf.h"
|
||||||
|
#include "libc/sysv/consts/sa.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/sysv/consts/ss.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stack overflow recovery technique #2
|
||||||
|
* longjmp out of signal back into thread
|
||||||
|
* simple but it can upset kernels / libraries
|
||||||
|
*/
|
||||||
|
|
||||||
|
jmp_buf recover;
|
||||||
|
volatile bool smashed_stack;
|
||||||
|
|
||||||
|
void CrashHandler(int sig, siginfo_t *si, void *ctx) {
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
||||||
|
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
||||||
|
smashed_stack = true;
|
||||||
|
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
||||||
|
longjmp(recover, 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
int StackOverflow(int f(), int n) {
|
||||||
|
if (n < INT_MAX) {
|
||||||
|
return f(f, n + 1) - 1;
|
||||||
|
} else {
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||||
|
|
||||||
|
void *MyPosixThread(void *arg) {
|
||||||
|
int jumpcode;
|
||||||
|
struct sigaction sa, o1, o2;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ss.ss_flags = 0;
|
||||||
|
ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096;
|
||||||
|
ss.ss_sp = gc(malloc(ss.ss_size));
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_sigaction = CrashHandler;
|
||||||
|
sigaction(SIGBUS, &sa, &o1);
|
||||||
|
sigaction(SIGSEGV, &sa, &o2);
|
||||||
|
if (!(jumpcode = setjmp(recover))) {
|
||||||
|
exit(pStackOverflow(pStackOverflow, 0));
|
||||||
|
}
|
||||||
|
ASSERT_EQ(123, jumpcode);
|
||||||
|
sigaction(SIGSEGV, &o2, 0);
|
||||||
|
sigaction(SIGBUS, &o1, 0);
|
||||||
|
// here's where longjmp() gets us into trouble
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
if (IsXnu() || IsNetbsd()) {
|
||||||
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags); // wut
|
||||||
|
} else {
|
||||||
|
ASSERT_EQ(0, ss.ss_flags);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(stackoverflow, standardStack_altStack_thread_longjmp) {
|
||||||
|
pthread_t th;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
smashed_stack = false;
|
||||||
|
pthread_create(&th, 0, MyPosixThread, 0);
|
||||||
|
pthread_join(th, 0);
|
||||||
|
ASSERT_TRUE(smashed_stack);
|
||||||
|
// this should be SS_DISABLE but ShowCrashReports() creates an alt stack
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(0, ss.ss_flags);
|
||||||
|
}
|
||||||
|
}
|
116
test/libc/thread/stackoverflow3_test.c
Normal file
116
test/libc/thread/stackoverflow3_test.c
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||||
|
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||||
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||||
|
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||||
|
│ │
|
||||||
|
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||||
|
│ any purpose with or without fee is hereby granted, provided that the │
|
||||||
|
│ above copyright notice and this permission notice appear in all copies. │
|
||||||
|
│ │
|
||||||
|
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||||
|
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||||
|
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||||
|
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||||
|
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||||
|
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||||
|
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||||
|
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||||
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
|
#include "libc/calls/struct/sigaction.h"
|
||||||
|
#include "libc/calls/struct/sigaltstack.h"
|
||||||
|
#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/limits.h"
|
||||||
|
#include "libc/mem/gc.internal.h"
|
||||||
|
#include "libc/mem/mem.h"
|
||||||
|
#include "libc/runtime/runtime.h"
|
||||||
|
#include "libc/runtime/sysconf.h"
|
||||||
|
#include "libc/sysv/consts/sa.h"
|
||||||
|
#include "libc/sysv/consts/sig.h"
|
||||||
|
#include "libc/sysv/consts/ss.h"
|
||||||
|
#include "libc/testlib/testlib.h"
|
||||||
|
#include "libc/thread/thread.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stack overflow recovery technique #3
|
||||||
|
* rewrite thread cpu state to call pthread_exit
|
||||||
|
* this method returns gracefully from signal handlers
|
||||||
|
* unfortunately it relies on cpu architecture knowledge
|
||||||
|
*/
|
||||||
|
|
||||||
|
volatile bool smashed_stack;
|
||||||
|
|
||||||
|
void Exiter(void *rc) {
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(0, ss.ss_flags);
|
||||||
|
pthread_exit(rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CrashHandler(int sig, siginfo_t *si, void *arg) {
|
||||||
|
ucontext_t *ctx = arg;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(SS_ONSTACK, ss.ss_flags);
|
||||||
|
kprintf("kprintf avoids overflowing %G %p\n", si->si_signo, si->si_addr);
|
||||||
|
smashed_stack = true;
|
||||||
|
ASSERT_TRUE(__is_stack_overflow(si, ctx));
|
||||||
|
//
|
||||||
|
// the backtrace will look like this
|
||||||
|
//
|
||||||
|
// 0x000000000042561d: pthread_exit at pthread_exit.c:99
|
||||||
|
// 0x0000000000418777: Exiter at stackoverflow2_test.c:40
|
||||||
|
// 0x00000000004186d8: CrashHandler at stackoverflow2_test.c:49
|
||||||
|
// 0x000000000041872a: StackOverflow at stackoverflow2_test.c:53
|
||||||
|
// 0x000000000041872a: StackOverflow at stackoverflow2_test.c:53
|
||||||
|
// 0x000000000041872a: StackOverflow at stackoverflow2_test.c:53
|
||||||
|
// ...
|
||||||
|
//
|
||||||
|
ctx->uc_mcontext.ARG0 = 123;
|
||||||
|
ctx->uc_mcontext.PC = (long)Exiter;
|
||||||
|
ctx->uc_mcontext.SP += 32768;
|
||||||
|
ctx->uc_mcontext.SP &= -16;
|
||||||
|
ctx->uc_mcontext.SP -= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StackOverflow(int f(), int n) {
|
||||||
|
if (n < INT_MAX) {
|
||||||
|
return f(f, n + 1) - 1;
|
||||||
|
} else {
|
||||||
|
return INT_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||||
|
|
||||||
|
void *MyPosixThread(void *arg) {
|
||||||
|
struct sigaction sa;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
ss.ss_flags = 0;
|
||||||
|
ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096;
|
||||||
|
ss.ss_sp = gc(malloc(ss.ss_size));
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(&ss, 0));
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_sigaction = CrashHandler;
|
||||||
|
sigaction(SIGBUS, &sa, 0);
|
||||||
|
sigaction(SIGSEGV, &sa, 0);
|
||||||
|
exit(pStackOverflow(pStackOverflow, 0));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(stackoverflow, standardStack_altStack_thread_teleport) {
|
||||||
|
void *res;
|
||||||
|
pthread_t th;
|
||||||
|
struct sigaltstack ss;
|
||||||
|
smashed_stack = false;
|
||||||
|
pthread_create(&th, 0, MyPosixThread, 0);
|
||||||
|
pthread_join(th, &res);
|
||||||
|
ASSERT_EQ((void *)123L, res);
|
||||||
|
ASSERT_TRUE(smashed_stack);
|
||||||
|
// this should be SS_DISABLE but ShowCrashReports() creates an alt stack
|
||||||
|
ASSERT_SYS(0, 0, sigaltstack(0, &ss));
|
||||||
|
ASSERT_EQ(0, ss.ss_flags);
|
||||||
|
}
|
2
third_party/make/job.c
vendored
2
third_party/make/job.c
vendored
|
@ -1939,7 +1939,7 @@ child_execute_job (struct childbase *child,
|
||||||
(STRING_SIZE_TUPLE (".PLEDGE"),
|
(STRING_SIZE_TUPLE (".PLEDGE"),
|
||||||
c ? c->file : 0, 0));
|
c ? c->file : 0, 0));
|
||||||
promises = ps ? xstrdup (ps) : 0;
|
promises = ps ? xstrdup (ps) : 0;
|
||||||
if (ParsePromises (promises, &ipromises))
|
if (ParsePromises (promises, &ipromises, 0))
|
||||||
{
|
{
|
||||||
OSS (error, NILF, "%s: invalid .PLEDGE string: %s",
|
OSS (error, NILF, "%s: invalid .PLEDGE string: %s",
|
||||||
argv[0], strerror (errno));
|
argv[0], strerror (errno));
|
||||||
|
|
|
@ -785,7 +785,7 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ParsePromises(g_promises, &ipromises) == -1) {
|
if (ParsePromises(g_promises, &ipromises, 0) == -1) {
|
||||||
kprintf("error: bad promises list: %s\n", g_promises);
|
kprintf("error: bad promises list: %s\n", g_promises);
|
||||||
_Exit(21);
|
_Exit(21);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue