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/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#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/log/check.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/prot.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"
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
if (n < N) {
|
||||
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 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[]) {
|
||||
if (IsWindows()) {
|
||||
fprintf(stderr, "stack overflow not possible to catch on windows yet\n");
|
||||
exit(1);
|
||||
}
|
||||
ShowCrashReports();
|
||||
return !!Ap(Ap, 0);
|
||||
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);
|
||||
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
|
||||
*/
|
||||
int ParsePromises(const char *promises, unsigned long *out) {
|
||||
int ParsePromises(const char *promises, unsigned long *out,
|
||||
unsigned long current) {
|
||||
int rc = 0;
|
||||
int promise;
|
||||
unsigned long ipromises;
|
||||
|
@ -57,7 +58,7 @@ int ParsePromises(const char *promises, unsigned long *out) {
|
|||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
ipromises = 0;
|
||||
ipromises = current;
|
||||
}
|
||||
if (!rc) {
|
||||
*out = ipromises;
|
||||
|
|
|
@ -239,9 +239,6 @@
|
|||
int pledge(const char *promises, const char *execpromises) {
|
||||
int e, rc;
|
||||
unsigned long ipromises, iexecpromises;
|
||||
if (promises && !execpromises) {
|
||||
execpromises = promises;
|
||||
}
|
||||
if (!promises) {
|
||||
// OpenBSD says NULL argument means it doesn't change, i.e.
|
||||
// pledge(0,0) on OpenBSD does nothing. The Cosmopolitan Libc
|
||||
|
@ -262,8 +259,8 @@ int pledge(const char *promises, const char *execpromises) {
|
|||
return -1;
|
||||
} else if (!IsTiny() && IsGenuineBlink()) {
|
||||
rc = 0; // blink doesn't support seccomp; avoid noisy log warnings
|
||||
} else if (!ParsePromises(promises, &ipromises) &&
|
||||
!ParsePromises(execpromises, &iexecpromises)) {
|
||||
} else if (!ParsePromises(promises, &ipromises, __promises) &&
|
||||
!ParsePromises(execpromises, &iexecpromises, __execpromises)) {
|
||||
if (IsLinux()) {
|
||||
// copy exec and execnative from promises to execpromises
|
||||
iexecpromises = ~(~iexecpromises | (~ipromises & (1ul << PROMISE_EXEC)));
|
||||
|
|
|
@ -14,7 +14,7 @@ struct Pledges {
|
|||
extern const struct Pledges kPledge[PROMISE_LEN_];
|
||||
|
||||
int sys_pledge_linux(unsigned long, int);
|
||||
int ParsePromises(const char *, unsigned long *);
|
||||
int ParsePromises(const char *, unsigned long *, unsigned long);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#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/intrin/atomic.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -72,9 +73,6 @@ struct ContextFrame {
|
|||
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) {
|
||||
return sig == SIGURG || //
|
||||
sig == SIGCONT || //
|
||||
|
@ -90,9 +88,21 @@ textwindows bool __sig_ignored(int sig) {
|
|||
|
||||
static textwindows bool __sig_should_use_altstack(unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
return (flags & SA_ONSTACK) && //
|
||||
tib->tib_sigstack_size && //
|
||||
!(tib->tib_sigstack_flags & (SS_DISABLE | SS_ONSTACK));
|
||||
if (!(flags & SA_ONSTACK)) {
|
||||
return false; // signal handler didn't enable it
|
||||
}
|
||||
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) {
|
||||
|
@ -129,20 +139,6 @@ static textwindows sigaction_f __sig_handler(unsigned 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) {
|
||||
unsigned rva, flags;
|
||||
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,
|
||||
__sig_handler(rva), DescribeSigset(0, &ctx.uc_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,
|
||||
__sig_handler(rva),
|
||||
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;
|
||||
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;
|
||||
pt->tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
||||
} else {
|
||||
sp = (nc.Rsp - 128 - sizeof(struct ContextFrame)) & -16;
|
||||
}
|
||||
|
@ -338,11 +332,11 @@ static int __sig_crash_sig(struct NtExceptionPointers *ep, int *code) {
|
|||
case kNtSignalPrivInstruction:
|
||||
*code = ILL_PRVOPC;
|
||||
return SIGILL;
|
||||
case kNtSignalGuardPage:
|
||||
case kNtSignalInPageError:
|
||||
case kNtStatusStackOverflow:
|
||||
*code = SEGV_MAPERR;
|
||||
return SIGSEGV;
|
||||
case kNtSignalGuardPage:
|
||||
case kNtSignalAccessViolation:
|
||||
*code = SEGV_ACCERR;
|
||||
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
|
||||
__msabi unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
||||
int code, sig = __sig_crash_sig(ep, &code);
|
||||
static void __sig_crasher(struct NtExceptionPointers *ep, int code, int sig,
|
||||
struct CosmoTib *tib) {
|
||||
|
||||
// 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 "
|
||||
"cosmoaddr2line %s %lx %s",
|
||||
ep->ExceptionRecord->ExceptionCode, sig, program_invocation_name,
|
||||
ep->ContextRecord->Rip,
|
||||
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
|
||||
if (sig == SIGTRAP) {
|
||||
ep->ContextRecord->Rip++;
|
||||
if (__sig_ignored(sig)) {
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
}
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
siginfo_t si = {.si_signo = sig,
|
||||
.si_code = code,
|
||||
.si_addr = ep->ExceptionRecord->ExceptionAddress};
|
||||
|
||||
// if the user didn't install a signal handler for this unmaskable
|
||||
// exception, then print a friendly helpful hint message to stderr
|
||||
unsigned rva = __sighandrvas[sig];
|
||||
unsigned flags = __sighandflags[sig];
|
||||
if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) {
|
||||
#ifndef TINY
|
||||
intptr_t hStderr;
|
||||
|
@ -418,16 +411,74 @@ __msabi unsigned __sig_crash(struct NtExceptionPointers *ep) {
|
|||
#endif
|
||||
__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) {
|
||||
STRACE("resetting %G handler", sig);
|
||||
__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};
|
||||
siginfo_t si = {.si_signo = sig, .si_code = code, .si_addr = si_addr};
|
||||
_ntcontext2linux(&ctx, ep->ContextRecord);
|
||||
ctx.uc_sigmask.__bits[0] = pt->tib->tib_sigmask;
|
||||
__sig_call(sig, &si, &ctx, rva, flags, pt->tib);
|
||||
pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
ctx.uc_sigmask.__bits[0] = tib->tib_sigmask;
|
||||
__sig_handler(rva)(sig, &si, &ctx);
|
||||
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -24,20 +24,19 @@
|
|||
|
||||
dontinstrument const char *(DescribeBacktrace)(char buf[N],
|
||||
struct StackFrame *fr) {
|
||||
bool gotsome = false;
|
||||
char *p = buf;
|
||||
char *pe = p + N;
|
||||
bool gotsome = false;
|
||||
while (fr) {
|
||||
if (gotsome) {
|
||||
if (p + 1 < pe) {
|
||||
if (p + 16 + 1 + 1 <= pe) {
|
||||
if (gotsome) {
|
||||
*p++ = ' ';
|
||||
*p = 0;
|
||||
} else {
|
||||
gotsome = true;
|
||||
}
|
||||
} else {
|
||||
gotsome = true;
|
||||
}
|
||||
if (p + 17 <= pe) {
|
||||
p = __hexcpy(p, fr->addr);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
fr = fr->next;
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ void __get_main_stack(void **out_addr, size_t *out_size, int *out_guardsize) {
|
|||
if (IsWindows()) {
|
||||
*out_addr = (void *)GetStaticStackAddr(0);
|
||||
*out_size = GetStaticStackSize();
|
||||
*out_guardsize = 4096;
|
||||
*out_guardsize = GetGuardSize();
|
||||
return;
|
||||
}
|
||||
int pagesz = getauxval(AT_PAGESZ);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include "libc/intrin/likely.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 _DATATRACE 1 /* 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_
|
||||
|
||||
extern bool __nocolor;
|
||||
extern bool _wantcrashreports;
|
||||
extern bool g_isrunningundermake;
|
||||
|
||||
void __start_fatal(const char *, int);
|
||||
|
|
|
@ -91,7 +91,6 @@ void ShowCrashReports(void) {
|
|||
InstallCrashHandler(SIGBUS, SA_RESETHAND);
|
||||
InstallCrashHandler(SIGABRT, SA_RESETHAND);
|
||||
InstallCrashHandler(SIGSEGV, SA_RESETHAND | SA_ONSTACK);
|
||||
_wantcrashreports = true;
|
||||
}
|
||||
|
||||
IGNORE_LEAKS(ShowCrashReports)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#define COSMOPOLITAN_LIBC_NT_STRUCT_NTEXCEPTIONRECORD_H_
|
||||
|
||||
#define kNtExceptionMaximumParameters 15
|
||||
#define kNtExceptionNoncontinuable 1
|
||||
#define kNtExceptionNoncontinuable 1
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
|
||||
|
@ -12,7 +12,7 @@ struct NtExceptionRecord {
|
|||
struct NtExceptionRecord *ExceptionRecord; /* nested exceptions */
|
||||
void *ExceptionAddress; /* %rip */
|
||||
uint32_t NumberParameters; /* #ExceptionInformation */
|
||||
uint32_t *ExceptionInformation[kNtExceptionMaximumParameters];
|
||||
uint64_t ExceptionInformation[kNtExceptionMaximumParameters];
|
||||
};
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "libc/intrin/bsr.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/internal.h"
|
||||
|
@ -38,6 +39,9 @@
|
|||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/mem.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/synchronization.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) {
|
||||
notpossible;
|
||||
}
|
||||
if (pt->attr.__guardsize && !IsWindows() &&
|
||||
mprotect(pt->attr.__stackaddr, pt->attr.__guardsize, PROT_NONE)) {
|
||||
notpossible;
|
||||
if (pt->attr.__guardsize) {
|
||||
if (!IsWindows()) {
|
||||
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;
|
||||
if (IsAsan() && pt->attr.__guardsize) {
|
||||
if (IsAsan() && !IsWindows() && pt->attr.__guardsize) {
|
||||
__asan_poison(pt->attr.__stackaddr, pt->attr.__guardsize,
|
||||
kAsanStackOverflow);
|
||||
}
|
||||
|
|
|
@ -552,6 +552,8 @@ TEST(pledge_openbsd, execpromisesIsNull_letsItDoAnything) {
|
|||
}
|
||||
EXPECT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_FALSE(WIFSIGNALED(ws));
|
||||
EXPECT_EQ(0, WTERMSIG(ws));
|
||||
EXPECT_EQ(3, WEXITSTATUS(ws));
|
||||
}
|
||||
|
||||
|
|
|
@ -255,58 +255,6 @@ TEST(pthread_cleanup, pthread_normal) {
|
|||
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
|
||||
|
||||
|
|
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"),
|
||||
c ? c->file : 0, 0));
|
||||
promises = ps ? xstrdup (ps) : 0;
|
||||
if (ParsePromises (promises, &ipromises))
|
||||
if (ParsePromises (promises, &ipromises, 0))
|
||||
{
|
||||
OSS (error, NILF, "%s: invalid .PLEDGE string: %s",
|
||||
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);
|
||||
_Exit(21);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue