Improve stack overflow recovery

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

View file

@ -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
*/