From 379cd770782a051c51fca0646bbb79732b29c6df Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 27 Dec 2024 01:03:11 -0800 Subject: [PATCH] Improve memory manager and signal handling On Windows, mmap() now chooses addresses transactionally. It reduces the risk of badness when interacting with the WIN32 memory manager. We don't throw darts anymore. There is also no more retry limit, since we recover from mystery maps more gracefully. The subroutine for combining adjacent maps has been rewritten for clarity. The print maps subroutine is better This change goes to great lengths to perfect the stack overflow code. On Windows you can now longjmp() out of a crash signal handler. Guard pages previously weren't being restored properly by the signal handler. That's fixed, so on Windows you can now handle a stack overflow multiple times. Great thought has been put into selecting the perfect SIGSTKSZ constants so you can save sigaltstack() memory. You can now use kprintf() with 512 bytes of stack available. The guard pages beneath the main stack are now recorded in the memory manager. This change fixes getcontext() so it works right with the %rax register. --- libc/calls/sigaltstack.c | 9 +- libc/calls/sigenter-xnu.c | 9 + libc/calls/struct/ucontext.internal.h | 8 +- libc/dlopen/dlopen.c | 8 +- libc/dlopen/stubs.c | 9 +- libc/intrin/directmap.c | 3 +- libc/intrin/getminsigstksz.c | 39 ++- libc/intrin/getsafesize.greg.c | 11 +- libc/intrin/kisdangerous.c | 4 +- libc/intrin/kprintf.greg.c | 30 +- libc/intrin/maps.c | 28 +- libc/intrin/maps.h | 9 +- libc/intrin/mmap.c | 377 +++++++++++++++--------- libc/intrin/mprotect.c | 1 - libc/intrin/ntcontext2linux.c | 82 ------ libc/intrin/printmaps.c | 48 ++- libc/intrin/sig.c | 236 +++++++++------ libc/intrin/stack.c | 12 +- libc/intrin/tailcontext.S | 3 +- libc/intrin/ucontext.c | 4 +- libc/log/backtrace3.c | 30 +- libc/log/oncrash_arm64.c | 6 - libc/nt/enum/status.h | 122 ++++---- libc/runtime/enable_tls.c | 15 +- libc/runtime/opensymboltable.greg.c | 6 +- libc/runtime/sigsetjmp.S | 2 +- libc/runtime/sysconf.c | 6 +- libc/runtime/zipos-get.c | 8 +- libc/sysv/consts.sh | 4 +- libc/sysv/consts/_MINSIGSTKSZ.S | 2 +- libc/sysv/consts/_SIGSTKSZ.S | 2 +- libc/sysv/consts/sig.h | 20 -- libc/sysv/consts/ss.h | 2 +- libc/testlib/testmain.c | 17 +- libc/thread/pthread_attr_setstacksize.c | 10 +- libc/thread/pthread_getattr_np.c | 16 - test/libc/calls/getcontext_test.c | 2 + test/libc/calls/sigaction_test.c | 7 +- test/libc/calls/sigaltstack_test.c | 13 + test/libc/calls/stackoverflow1_test.c | 62 ++-- test/libc/calls/stackoverflow2_test.c | 70 +++-- test/libc/calls/stackoverflow3_test.c | 4 +- test/libc/calls/stackoverflow4_test.c | 4 +- test/libc/calls/stackoverflow5_test.c | 2 +- test/libc/intrin/BUILD.mk | 9 + test/libc/intrin/mmap_test.c | 4 +- test/libc/intrin/mprotect_test.c | 9 +- test/posix/signal_latency_async_test.c | 4 + 48 files changed, 826 insertions(+), 562 deletions(-) delete mode 100644 libc/intrin/ntcontext2linux.c diff --git a/libc/calls/sigaltstack.c b/libc/calls/sigaltstack.c index dac5f4526ad..a580a0fecfc 100644 --- a/libc/calls/sigaltstack.c +++ b/libc/calls/sigaltstack.c @@ -113,7 +113,7 @@ static int sigaltstack_bsd(const struct sigaltstack *neu, * struct sigaction sa; * struct sigaltstack ss; * ss.ss_flags = 0; - * ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192; + * ss.ss_size = sysconf(_SC_SIGSTKSZ); * ss.ss_sp = malloc(ss.ss_size); * sigaltstack(&ss, 0); * sigemptyset(&sa.ss_mask); @@ -121,11 +121,16 @@ static int sigaltstack_bsd(const struct sigaltstack *neu, * sa.sa_handler = OnStackOverflow; * sigaction(SIGSEGV, &sa, 0); * + * Your stack size should be `sysconf(_SC_SIGSTKSZ)` which should be + * somewhere in the ballpark of 32kb to 64kb. You should go no lower + * than `sysconf(_SC_MINSIGSTKSZ) + 2048` which could be 4kb - 34kb. + * Cosmo also defines `SIGSTKSZ` as 32kb, which should also be safe. + * * @param neu if non-null will install new signal alt stack * @param old if non-null will receive current signal alt stack * @return 0 on success, or -1 w/ errno * @raise EFAULT if bad memory was supplied - * @raise ENOMEM if `neu->ss_size` is less than `MINSIGSTKSZ` + * @raise ENOMEM if `neu->ss_size` is beneath `sysconf(_SC_MINSIGSTKSZ)` */ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) { int rc; diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index c68a9c7c563..9d546ff2851 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -33,6 +33,7 @@ #include "libc/runtime/syslib.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" /** * @fileoverview XNU kernel callback normalization. @@ -513,6 +514,7 @@ privileged void __sigenter_xnu(int sig, struct siginfo_xnu *xnuinfo, flags = __sighandflags[sig]; #ifdef __aarch64__ + // xnu silicon claims to support sa_resethand but it does nothing // this can be tested, since it clears the bit from flags as well if (flags & SA_RESETHAND) { @@ -521,6 +523,13 @@ privileged void __sigenter_xnu(int sig, struct siginfo_xnu *xnuinfo, __sighandflags[sig] = 0; __sighandrvas[sig] = 0; } + + // unlike amd64, the instruction pointer on arm64 isn't advanced + // past the debugger breakpoint instruction automatically. we need + // this so execution can resume after __builtin_trap(). + if (xnuctx && sig == SIGTRAP) + xnuctx->uc_mcontext->__ss.__pc += 4; + #endif if (~flags & SA_SIGINFO) { diff --git a/libc/calls/struct/ucontext.internal.h b/libc/calls/struct/ucontext.internal.h index 9122af24a40..18a271f1002 100644 --- a/libc/calls/struct/ucontext.internal.h +++ b/libc/calls/struct/ucontext.internal.h @@ -1,13 +1,14 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ #include "libc/calls/ucontext.h" -#include "libc/nt/struct/context.h" COSMOPOLITAN_C_START_ #ifdef __x86_64__ #define PC rip #define SP rsp #define BP rbp +#define RES0 rax +#define RES1 rdx #define ARG0 rdi #define ARG1 rsi #define ARG2 rdx @@ -18,6 +19,8 @@ COSMOPOLITAN_C_START_ #define PC pc #define SP sp #define BP regs[29] +#define RES0 regs[0] +#define RES1 regs[1] #define ARG0 regs[0] #define ARG1 regs[1] #define ARG2 regs[2] @@ -28,8 +31,5 @@ COSMOPOLITAN_C_START_ #error "unsupported architecture" #endif -void _ntcontext2linux(struct ucontext *, const struct NtContext *); -void _ntlinux2context(struct NtContext *, const ucontext_t *); - COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_UCONTEXT_INTERNAL_H_ */ diff --git a/libc/dlopen/dlopen.c b/libc/dlopen/dlopen.c index 57767d7bb1d..5216aba98cf 100644 --- a/libc/dlopen/dlopen.c +++ b/libc/dlopen/dlopen.c @@ -810,7 +810,7 @@ void *cosmo_dlopen(const char *path, int mode) { } ALLOW_CANCELATION; ALLOW_SIGNALS; - STRACE("dlopen(%#s, %d) → %p% m", path, mode, res); + STRACE("cosmo_dlopen(%#s, %d) → %p% m", path, mode, res); return res; } @@ -855,7 +855,7 @@ void *cosmo_dlsym(void *handle, const char *name) { } else { func = 0; } - STRACE("dlsym(%p, %#s) → %p", handle, name, func); + STRACE("cosmo_dlsym(%p, %#s) → %p", handle, name, func); return func; } @@ -890,7 +890,7 @@ int cosmo_dlclose(void *handle) { } else { res = -1; } - STRACE("dlclose(%p) → %d", handle, res); + STRACE("cosmo_dlclose(%p) → %d", handle, res); return res; } @@ -909,6 +909,6 @@ char *cosmo_dlerror(void) { } else { res = dlerror_buf; } - STRACE("dlerror() → %#s", res); + STRACE("cosmo_dlerror() → %#s", res); return res; } diff --git a/libc/dlopen/stubs.c b/libc/dlopen/stubs.c index 8dad1af0500..357f864f3ee 100644 --- a/libc/dlopen/stubs.c +++ b/libc/dlopen/stubs.c @@ -17,6 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dlopen/dlfcn.h" +#include "libc/intrin/strace.h" + +#define DLOPEN_ERROR \ + "dlopen() isn't supported; consider using cosmo_dlopen() and read its docs" /** * Opens dynamic shared object using host platform libc. @@ -27,12 +31,13 @@ * * @return null always */ -void *dlopen(const char *, int) { +void *dlopen(const char *path, int mode) { + STRACE("dlopen(%#s, %d) → 0 [%s]", path, mode, DLOPEN_ERROR); return 0; } char *dlerror(void) { - return "dlopen() isn't supported by cosmo; try using cosmo_dlopen()"; + return DLOPEN_ERROR; } void *dlsym(void *, const char *) { diff --git a/libc/intrin/directmap.c b/libc/intrin/directmap.c index b0a40ff596f..aa1e4e76c87 100644 --- a/libc/intrin/directmap.c +++ b/libc/intrin/directmap.c @@ -16,12 +16,13 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/directmap.h" #include "libc/calls/calls.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/describebacktrace.h" #include "libc/intrin/describeflags.h" -#include "libc/intrin/directmap.h" #include "libc/intrin/strace.h" #include "libc/nt/runtime.h" #include "libc/runtime/memtrack.internal.h" diff --git a/libc/intrin/getminsigstksz.c b/libc/intrin/getminsigstksz.c index 52c6aab6629..9b746e27917 100644 --- a/libc/intrin/getminsigstksz.c +++ b/libc/intrin/getminsigstksz.c @@ -16,18 +16,47 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/ucontext.h" +#include "libc/dce.h" #include "libc/intrin/getauxval.h" -#include "libc/macros.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/ss.h" long __get_minsigstksz(void) { - struct AuxiliaryValue x; - x = __getauxval(AT_MINSIGSTKSZ); - if (x.isfound) { - return MAX(_MINSIGSTKSZ - 1024, x.value) + 1024; + struct AuxiliaryValue av; + av = __getauxval(AT_MINSIGSTKSZ); + if (av.isfound) { + long res = av.value; + if (!IsLinux()) + res += sizeof(struct ucontext) + sizeof(struct siginfo) + 128; + if (res < _MINSIGSTKSZ) + res = _MINSIGSTKSZ; + return res; } else { + // _MINSIGSTKSZ takes these things into consideration: + // + // 1. The platform definition of MINSIGSTKSZ. This will probably be + // enforced by the kernel when calling sys_sigaltstack(). On ARM + // platforms this might be several kilobytes larger than x86. On + // Linux they really want you to use AT_MINSIGSTKSZ instead. The + // kernel should ideally set this to be the number of bytes that + // get subtracted from the stack pointer when delivering signals + // meaning that if you use this for a stack size your handler is + // called successfully but if it uses the stack then it'll crash + // + // 2. Cosmo sigenter overhead. On non-Linux OSes the kernel calls a + // trampoline in the libc runtime, which translates the platform + // specific signal frame to the Linux memory layout. It means we + // need to push ~1024 extra bytes on the stack to call a handler + // + // 3. Sanity testing. Assume we use sysconf(_SC_MINSIGSTKSZ) + 2048 + // as our stack size (see stackoverflow1_test.c). Then we should + // have enough room to use kprintf() from our signal handler. If + // that isn't the case, then this should be increased a bit more + // noting that if 1024 is used then kprintf should print refusal + // return _MINSIGSTKSZ; } } diff --git a/libc/intrin/getsafesize.greg.c b/libc/intrin/getsafesize.greg.c index c7735cd1f3d..5a6d9123b6b 100644 --- a/libc/intrin/getsafesize.greg.c +++ b/libc/intrin/getsafesize.greg.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/sections.internal.h" -#include "libc/intrin/kprintf.h" #include "libc/runtime/memtrack.internal.h" +#include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/tls.h" @@ -37,12 +37,13 @@ privileged optimizesize long __get_safe_size(long want, long extraspace) { struct PosixThread *pt; struct CosmoTib *tib = __get_tls_privileged(); long bottom, sp = GetStackPointer(); - if ((char *)sp >= tib->tib_sigstack_addr && - (char *)sp <= tib->tib_sigstack_addr + tib->tib_sigstack_size) { + if (sp >= (long)tib->tib_sigstack_addr && + sp < (long)tib->tib_sigstack_addr + tib->tib_sigstack_size) { bottom = (long)tib->tib_sigstack_addr; } else if ((pt = (struct PosixThread *)tib->tib_pthread) && - pt->pt_attr.__stacksize) { - bottom = (long)pt->pt_attr.__stackaddr + pt->pt_attr.__guardsize; + sp >= (long)pt->pt_attr.__stackaddr && + sp < (long)pt->pt_attr.__stackaddr + pt->pt_attr.__stacksize) { + bottom = (long)pt->pt_attr.__stackaddr; } else { return want; } diff --git a/libc/intrin/kisdangerous.c b/libc/intrin/kisdangerous.c index 62872425e46..2672eae0dbf 100644 --- a/libc/intrin/kisdangerous.c +++ b/libc/intrin/kisdangerous.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/kprintf.h" #include "libc/intrin/maps.h" +#include "libc/runtime/runtime.h" privileged optimizesize bool32 kisdangerous(const void *addr) { bool32 res = true; @@ -26,7 +27,8 @@ privileged optimizesize bool32 kisdangerous(const void *addr) { struct Map *map; if ((map = __maps_floor(addr))) if ((const char *)addr >= map->addr && - (const char *)addr < map->addr + map->size) + (const char *)addr < + map->addr + ((map->size + __pagesize - 1) & -__pagesize)) res = false; } else { res = false; diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 283aa71ddb5..eb70ce94f7d 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -352,9 +352,8 @@ ABI void klog(const char *b, size_t n) { long h; uint32_t wrote; long rax, rdi, rsi, rdx; - if ((h = kloghandle()) == -1) { + if ((h = kloghandle()) == -1) return; - } if (IsWindows()) { bool32 ok; intptr_t ev; @@ -408,10 +407,11 @@ ABI void klog(const char *b, size_t n) { ABI static size_t kformat(char *b, size_t n, const char *fmt, va_list va) { int si; wint_t t, u; + char *cxxbuf; const char *abet; signed char type; const char *s, *f; - char cxxbuf[3000]; + int cxxbufsize = 0; struct CosmoTib *tib; unsigned long long x; unsigned i, j, m, rem, sign, hash, cols, prec; @@ -755,13 +755,25 @@ ABI static size_t kformat(char *b, size_t n, const char *fmt, va_list va) { x = va_arg(va, intptr_t); if (_weaken(__symtab) && *_weaken(__symtab) && (idx = _weaken(__get_symbol)(0, x)) != -1) { - /* if (p + 1 <= e) */ - /* *p++ = '&'; */ s = (*_weaken(__symtab))->name_base + (*_weaken(__symtab))->names[idx]; - if (_weaken(__is_mangled) && _weaken(__is_mangled)(s) && - _weaken(__demangle)(cxxbuf, s, sizeof(cxxbuf)) != -1) - s = cxxbuf; +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + // decipher c++ symbols if there's enough stack memory + // stack size requirement assumes max_depth's still 20 + if (_weaken(__demangle) && // + _weaken(__is_mangled) && // + _weaken(__is_mangled)(s)) { + if (!cxxbufsize) + if ((cxxbufsize = __get_safe_size(8192, 8192)) >= 512) { + cxxbuf = alloca(cxxbufsize); + CheckLargeStackAllocation(cxxbuf, sizeof(cxxbufsize)); + } + if (cxxbufsize >= 512) + if (_weaken(__demangle)(cxxbuf, s, cxxbufsize) != -1) + s = cxxbuf; + } +#pragma GCC pop_options goto FormatString; } base = 4; @@ -1050,7 +1062,7 @@ ABI size_t kvsnprintf(char *b, size_t n, const char *fmt, va_list v) { ABI void kvprintf(const char *fmt, va_list v) { #pragma GCC push_options #pragma GCC diagnostic ignored "-Walloca-larger-than=" - long size = __get_safe_size(8000, 8000); + long size = __get_safe_size(8192, 2048); if (size < 80) { klog(STACK_ERROR, sizeof(STACK_ERROR) - 1); return; diff --git a/libc/intrin/maps.c b/libc/intrin/maps.c index 8379f8cf157..e9b7d8aa0d1 100644 --- a/libc/intrin/maps.c +++ b/libc/intrin/maps.c @@ -19,12 +19,14 @@ #include "libc/intrin/maps.h" #include "ape/sections.internal.h" #include "libc/calls/state.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/cosmo.h" #include "libc/dce.h" #include "libc/intrin/describebacktrace.h" #include "libc/intrin/dll.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/maps.h" +#include "libc/macros.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" @@ -72,16 +74,30 @@ void __maps_stack(char *stackaddr, int pagesz, int guardsize, size_t stacksize, void __maps_init(void) { int pagesz = __pagesize; - // initialize lemur64 rng + // initialize lemur64 __maps.rand = 2131259787901769494; - __maps.rand ^= rdtsc(); + __maps.rand ^= kStartTsc; + + // these static map objects avoid mandatory mmap() in __maps_alloc() + // they aren't actually needed for bootstrapping this memory manager + for (int i = 0; i < ARRAYLEN(__maps.spool); ++i) + __maps_free(&__maps.spool[i]); // record _start() stack mapping if (!IsWindows()) { - struct AddrSize stack; - stack = __get_main_stack(); - __maps_stack(stack.addr, pagesz, 0, stack.size, (uintptr_t)ape_stack_prot, - 0); + + // linux v4.12+ reserves 1mb of guard space beneath rlimit_stack + // https://lwn.net/Articles/725832/. if we guess too small, then + // slackmap will create a bunch of zombie stacks in __print_maps + // to coverup the undisclosed memory but no cost if we guess big + size_t guardsize = (__maps.rand % 8 + 1) * 1000 * 1024; + guardsize += __pagesize - 1; + guardsize &= -__pagesize; + + // track the main stack region that the os gave to start earlier + struct AddrSize stack = __get_main_stack(); + __maps_stack(stack.addr - guardsize, pagesz, guardsize, + guardsize + stack.size, (uintptr_t)ape_stack_prot, 0); } // record .text and .data mappings diff --git a/libc/intrin/maps.h b/libc/intrin/maps.h index c8291f6ac9b..6623d91486a 100644 --- a/libc/intrin/maps.h +++ b/libc/intrin/maps.h @@ -37,9 +37,9 @@ struct Maps { _Atomic(uintptr_t) freed; size_t count; size_t pages; - char *pick; struct Map stack; struct Map guard; + struct Map spool[13]; }; struct AddrSize { @@ -93,5 +93,12 @@ static inline struct Map *__maps_first(void) { return 0; } +static inline struct Map *__maps_last(void) { + struct Tree *node; + if ((node = tree_last(__maps.maps))) + return MAP_TREE_CONTAINER(node); + return 0; +} + COSMOPOLITAN_C_END_ #endif /* COSMOPOLITAN_MAPS_H_ */ diff --git a/libc/intrin/mmap.c b/libc/intrin/mmap.c index 57dec7216c8..f8baf51ad38 100644 --- a/libc/intrin/mmap.c +++ b/libc/intrin/mmap.c @@ -30,10 +30,13 @@ #include "libc/intrin/strace.h" #include "libc/intrin/tree.h" #include "libc/intrin/weaken.h" +#include "libc/limits.h" +#include "libc/macros.h" #include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" #include "libc/runtime/zipos.internal.h" +#include "libc/stdckdint.h" #include "libc/stdio/sysparam.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/mremap.h" @@ -41,9 +44,8 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" -#define MMDEBUG 0 -#define MAX_SIZE 0x0ff800000000ul -#define MAX_TRIES 50 +#define MMDEBUG 0 +#define MAX_SIZE 0x0ff800000000ul #define MAP_FIXED_NOREPLACE_linux 0x100000 @@ -256,52 +258,101 @@ static void __maps_free_all(struct Map *list) { } } +static int __maps_funge_prot(int prot) { + prot &= ~MAP_FIXED; + prot &= ~MAP_FIXED_NOREPLACE; + return prot; +} + +static int __maps_funge_flags(int flags) { + if ((flags & MAP_TYPE) == MAP_SHARED_VALIDATE) { + flags &= ~MAP_TYPE; + flags |= MAP_SHARED; + } + return flags; +} + +static bool __maps_fungible(const struct Map *map) { + // anonymous memory is fungible on unix, so we may coalesce such + // mappings in the rbtree to have fewer objects. on windows even + // anonymous memory has unique win32 handles we need to preserve + return !IsWindows() && (map->flags & MAP_ANONYMOUS); +} + +static bool __maps_adjacent(const struct Map *x, const struct Map *y) { + char *a = x->addr + ((x->size + __pagesize - 1) & -__pagesize); + char *b = y->addr; + ASSERT(a <= b); + return a == b; +} + +static bool __maps_mergeable(const struct Map *x, const struct Map *y) { + if (!__maps_fungible(x)) + return false; + if (!__maps_fungible(y)) + return false; + if (!__maps_adjacent(x, y)) + return false; + if (__maps_funge_prot(x->prot) != __maps_funge_prot(y->prot)) + return false; + if (__maps_funge_flags(x->flags) != __maps_funge_flags(y->flags)) + return false; + return true; +} + void __maps_insert(struct Map *map) { + struct Map *left, *right; + ASSERT(map->size); + ASSERT(!__maps_overlaps(map->addr, map->size)); map->flags &= MAP_TYPE | MAP_ANONYMOUS | MAP_NOFORK; + __maps.pages += (map->size + __pagesize - 1) / __pagesize; - // coalesce adjacent mappings - if (!IsWindows() && (map->flags & MAP_ANONYMOUS)) { - int prot = map->prot & ~(MAP_FIXED | MAP_FIXED_NOREPLACE); - int flags = map->flags; - bool coalesced = false; - struct Map *floor, *other, *last = 0; - for (other = floor = __maps_floor(map->addr); - other && other->addr <= map->addr + map->size; - last = other, other = __maps_next(other)) { - if (prot == other->prot && flags == other->flags) { - if (!coalesced) { - if (map->addr == other->addr + other->size) { - __maps.pages += (map->size + __pagesize - 1) / __pagesize; - other->size += map->size; - __maps_free(map); - __maps_check(); - coalesced = true; - } else if (map->addr + map->size == other->addr) { - __maps.pages += (map->size + __pagesize - 1) / __pagesize; - other->addr -= map->size; - other->size += map->size; - __maps_free(map); - __maps_check(); - coalesced = true; - } - } - if (last && other->addr == last->addr + last->size) { - other->addr -= last->size; - other->size += last->size; - tree_remove(&__maps.maps, &last->tree); - __maps.count -= 1; - __maps_free(last); - __maps_check(); - } - } - } - if (coalesced) - return; + // find adjacent mappings + if ((left = __maps_floor(map->addr))) { + right = __maps_next(left); + } else { + right = __maps_first(); } - // otherwise insert new mapping - __maps.pages += (map->size + __pagesize - 1) / __pagesize; - __maps_add(map); + // avoid insert by making mapping on left bigger + if (left) + if (__maps_mergeable(left, map)) { + left->size += __pagesize - 1; + left->size &= -__pagesize; + left->size += map->size; + __maps_free(map); + map = 0; + } + + // avoid insert by making mapping on right bigger + if (map && right) + if (__maps_mergeable(map, right)) { + map->size += __pagesize - 1; + map->size &= -__pagesize; + right->addr -= map->size; + right->size += map->size; + __maps_free(map); + map = 0; + } + + // check if we filled a hole + if (!map && left && right) + if (__maps_mergeable(left, right)) { + left->size += __pagesize - 1; + left->size &= -__pagesize; + right->addr -= left->size; + right->size += left->size; + tree_remove(&__maps.maps, &left->tree); + __maps.count -= 1; + __maps_free(left); + map = 0; + } + + // otherwise just insert + if (map) + __maps_add(map); + + // sanity check __maps_check(); } @@ -313,7 +364,6 @@ static void __maps_track_insert(struct Map *map, char *addr, size_t size, map->flags = flags; map->hand = map_handle; __maps_lock(); - ASSERT(!__maps_overlaps(addr, size)); __maps_insert(map); __maps_unlock(); } @@ -349,16 +399,27 @@ struct Map *__maps_alloc(void) { return map; pthread_pause_np(); } - int gransz = __gransize; - struct DirectMap sys = sys_mmap(0, gransz, PROT_READ | PROT_WRITE, + void *mark; + int size = 65536; + __maps_lock(); + do { + // we're creating sudden surprise memory. the user might be in the + // middle of carefully planning a fixed memory structure. we don't + // want the system allocator to put our surprise memory inside it. + mark = __maps_randaddr(); + } while (__maps_overlaps(mark, size)); + struct DirectMap sys = sys_mmap(mark, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (sys.addr == MAP_FAILED) + if (sys.addr == MAP_FAILED) { + __maps_unlock(); return 0; + } map = sys.addr; - __maps_track_insert(map, sys.addr, gransz, sys.maphandle, + __maps_track_insert(map, sys.addr, size, sys.maphandle, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NOFORK); - for (int i = 1; i < gransz / sizeof(struct Map); ++i) + __maps_unlock(); + for (int i = 1; i < size / sizeof(struct Map); ++i) __maps_free(map + i); return MAPS_RETRY; } @@ -366,24 +427,18 @@ struct Map *__maps_alloc(void) { static int __munmap(char *addr, size_t size) { // validate arguments - int pagesz = __pagesize; - int gransz = __gransize; - if (((uintptr_t)addr & (gransz - 1)) || // + if (((uintptr_t)addr & (__gransize - 1)) || // !size || (uintptr_t)addr + size < size) return einval(); // lock the memory manager - // abort on reentry due to signal handler - if (__maps_lock()) { - __maps_unlock(); - return edeadlk(); - } + __maps_lock(); __maps_check(); // normalize size // abort if size doesn't include all pages in granule - size_t pgup_size = (size + pagesz - 1) & -pagesz; - size_t grup_size = (size + gransz - 1) & -gransz; + size_t pgup_size = (size + __pagesize - 1) & -__pagesize; + size_t grup_size = (size + __gransize - 1) & -__gransize; if (grup_size > pgup_size) if (__maps_overlaps(addr + pgup_size, grup_size - pgup_size)) { __maps_unlock(); @@ -393,7 +448,7 @@ static int __munmap(char *addr, size_t size) { // untrack mappings int rc; struct Map *deleted = 0; - rc = __muntrack(addr, pgup_size, pagesz, &deleted); + rc = __muntrack(addr, pgup_size, __pagesize, &deleted); __maps_unlock(); // delete mappings @@ -402,7 +457,7 @@ static int __munmap(char *addr, size_t size) { if (sys_munmap(map->addr, map->size)) rc = -1; } else if (map->hand != -1) { - ASSERT(!((uintptr_t)map->addr & (gransz - 1))); + ASSERT(!((uintptr_t)map->addr & (__gransize - 1))); if (!UnmapViewOfFile(map->addr)) rc = -1; if (!CloseHandle(map->hand)) @@ -428,25 +483,51 @@ void *__maps_randaddr(void) { } static void *__maps_pickaddr(size_t size) { - char *addr; - __maps_lock(); - for (int try = 0; try < MAX_TRIES; ++try) { - addr = __maps.pick; - __maps.pick = 0; - if (!addr) - addr = __maps_randaddr(); - if (!__maps_overlaps(addr, size)) { - __maps.pick = addr + ((size + __gransize - 1) & -__gransize); - __maps_unlock(); - return addr; + char *addr = 0; + struct Map *map, *prev; + size += __gransize - 1; + size &= -__gransize; + if ((map = __maps_last())) { + // choose address beneath higher mapping + for (; map; map = prev) { + char *min = (char *)(intptr_t)__gransize; + if ((prev = __maps_prev(map))) + min = prev->addr + ((prev->size + __gransize - 1) & -__gransize); + if (map->addr > min && // + map->addr - min >= size) { + addr = map->addr - size; + break; + } + } + // append if existing maps are too dense + if (!addr) { + map = __maps_last(); + addr = map->addr + ((map->size + __gransize - 1) & -__gransize); + intptr_t end = (intptr_t)addr; + if (ckd_add(&end, end, size)) + return 0; } + } else { + // roll the dice if rbtree is empty + addr = __maps_randaddr(); } - __maps_unlock(); - return 0; + return addr; } -static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd, - int64_t off, int pagesz, int gransz) { +static void *__mmap_impl(char *addr, size_t size, int prot, int flags, int fd, + int64_t off) { + + // validate file map args + if (!(flags & MAP_ANONYMOUS)) { + if (off & (__gransize - 1)) + return (void *)einval(); + if (IsWindows()) { + if (!__isfdkind(fd, kFdFile)) + return (void *)eacces(); + if ((g_fds.p[fd].flags & O_ACCMODE) == O_WRONLY) + return (void *)eacces(); + } + } // allocate Map object struct Map *map; @@ -458,7 +539,7 @@ static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd, // polyfill nuances of fixed mappings int sysflags = flags; bool noreplace = false; - bool should_untrack = false; + bool fixedmode = false; if (flags & MAP_FIXED_NOREPLACE) { if (flags & MAP_FIXED) { __maps_free(map); @@ -478,30 +559,78 @@ static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd, noreplace = true; } } else if (flags & MAP_FIXED) { - should_untrack = true; + fixedmode = true; } - // remove mapping we blew away - if (IsWindows() && should_untrack) - __munmap(addr, size); - - // obtain mapping from operating system + // loop for memory int olderr = errno; - int tries = MAX_TRIES; struct DirectMap res; -TryAgain: - res = sys_mmap(addr, size, prot, sysflags, fd, off); - if (res.addr == MAP_FAILED) { - if (IsWindows() && errno == EADDRNOTAVAIL) { - if (noreplace) { - errno = EEXIST; - } else if (should_untrack) { - errno = ENOMEM; - } else if (--tries && (addr = __maps_pickaddr(size))) { - errno = olderr; - goto TryAgain; + for (;;) { + + // transactionally find the mark on windows + if (IsWindows()) { + __maps_lock(); + if (!fixedmode) { + // give user desired address if possible + if (addr && __maps_overlaps(addr, size)) { + if (noreplace) { + __maps_unlock(); + __maps_free(map); + return (void *)eexist(); + } + addr = 0; + } + // choose suitable address then claim it in our rbtree + if (!addr && !(addr = __maps_pickaddr(size))) { + __maps_unlock(); + __maps_free(map); + return (void *)enomem(); + } + } else { + // remove existing mappings and their tracking objects + if (__munmap(addr, size)) { + __maps_unlock(); + __maps_free(map); + return (void *)enomem(); + } + } + // claims intended interval while still holding the lock + if (!__maps_track(addr, size, 0, 0)) { + __maps_unlock(); + __maps_free(map); + return (void *)enomem(); + } + __maps_unlock(); + } + + // ask operating system for our memory + // notice how we're not holding the lock + res = sys_mmap(addr, size, prot, sysflags, fd, off); + if (res.addr != MAP_FAILED) + break; + + // handle failure + if (IsWindows()) { + if (errno == EADDRNOTAVAIL) { + // we've encountered mystery memory + if (fixedmode) { + // TODO(jart): Use VirtualQuery() to destroy mystery memory. + __maps_untrack(addr, size); + errno = ENOMEM; + } else if (noreplace) { + // we can't try again with a different address in this case + __maps_untrack(addr, size); + errno = EEXIST; + } else { + // we shall leak the tracking object since it should at least + // partially cover the mystery mapping. so if we loop forever + // the system should eventually recover and find fresh spaces + errno = olderr; + addr = 0; + continue; + } } else { - errno = ENOMEM; + __maps_untrack(addr, size); } } __maps_free(map); @@ -509,24 +638,14 @@ static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd, } // polyfill map fixed noreplace - // we assume non-linux gives us addr if it's freed - // that's what linux (e.g. rhel7) did before noreplace if (noreplace && res.addr != addr) { - if (!IsWindows()) { - sys_munmap(res.addr, size); - } else { - UnmapViewOfFile(res.addr); - CloseHandle(res.maphandle); - } + ASSERT(!IsWindows()); + sys_munmap(res.addr, size); __maps_free(map); return (void *)eexist(); } - // untrack mapping we blew away - if (!IsWindows() && should_untrack) - __maps_untrack(res.addr, size); - - // track map object + // setup map object map->addr = res.addr; map->size = size; map->off = off; @@ -538,47 +657,23 @@ static void *__mmap_chunk(void *addr, size_t size, int prot, int flags, int fd, map->readonlyfile = (flags & MAP_TYPE) == MAP_SHARED && fd != -1 && (g_fds.p[fd].flags & O_ACCMODE) == O_RDONLY; } + + // track map object __maps_lock(); + if (IsWindows() || fixedmode) + __maps_untrack(res.addr, size); __maps_insert(map); __maps_unlock(); return res.addr; } -static void *__mmap_impl(char *addr, size_t size, int prot, int flags, int fd, - int64_t off, int pagesz, int gransz) { - - // validate file map args - if (!(flags & MAP_ANONYMOUS)) { - if (off & (gransz - 1)) - return (void *)einval(); - if (IsWindows()) { - if (!__isfdkind(fd, kFdFile)) - return (void *)eacces(); - if ((g_fds.p[fd].flags & O_ACCMODE) == O_WRONLY) - return (void *)eacces(); - } - } - - // try to pick our own addresses on windows which are higher up in the - // vaspace. this is important so that conflicts are less likely, after - // forking when resurrecting mappings, because win32 has a strong pref - // with lower memory addresses which may get assigned to who knows wut - if (IsWindows() && !addr) - if (!(addr = __maps_pickaddr(size))) - return (void *)enomem(); - - return __mmap_chunk(addr, size, prot, flags, fd, off, pagesz, gransz); -} - static void *__mmap(char *addr, size_t size, int prot, int flags, int fd, int64_t off) { char *res; - int pagesz = __pagesize; - int gransz = __gransize; // validate arguments - if ((uintptr_t)addr & (gransz - 1)) + if ((uintptr_t)addr & (__gransize - 1)) addr = NULL; if (!addr && (flags & (MAP_FIXED | MAP_FIXED_NOREPLACE))) return (void *)eperm(); @@ -588,12 +683,12 @@ static void *__mmap(char *addr, size_t size, int prot, int flags, int fd, return (void *)einval(); if (size > MAX_SIZE) return (void *)enomem(); - if (__maps.count * pagesz + size > __virtualmax) + if (__maps.count * __pagesize + size > __virtualmax) return (void *)enomem(); // create memory mappping if (!__isfdkind(fd, kFdZip)) { - res = __mmap_impl(addr, size, prot, flags, fd, off, pagesz, gransz); + res = __mmap_impl(addr, size, prot, flags, fd, off); } else { res = _weaken(__zipos_mmap)( addr, size, prot, flags, diff --git a/libc/intrin/mprotect.c b/libc/intrin/mprotect.c index 784906accff..b7b403afbb9 100644 --- a/libc/intrin/mprotect.c +++ b/libc/intrin/mprotect.c @@ -22,7 +22,6 @@ #include "libc/intrin/describeflags.h" #include "libc/intrin/directmap.h" #include "libc/intrin/dll.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/maps.h" #include "libc/intrin/strace.h" #include "libc/intrin/tree.h" diff --git a/libc/intrin/ntcontext2linux.c b/libc/intrin/ntcontext2linux.c deleted file mode 100644 index bf9d3df1569..00000000000 --- a/libc/intrin/ntcontext2linux.c +++ /dev/null @@ -1,82 +0,0 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 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/ucontext.h" -#include "libc/log/libfatal.internal.h" -#include "libc/nt/struct/context.h" -#include "libc/str/str.h" -#ifdef __x86_64__ - -textwindows void _ntcontext2linux(ucontext_t *ctx, const struct NtContext *cr) { - if (!cr) - return; - ctx->uc_mcontext.eflags = cr->EFlags; - ctx->uc_mcontext.rax = cr->Rax; - ctx->uc_mcontext.rbx = cr->Rbx; - ctx->uc_mcontext.rcx = cr->Rcx; - ctx->uc_mcontext.rdx = cr->Rdx; - ctx->uc_mcontext.rdi = cr->Rdi; - ctx->uc_mcontext.rsi = cr->Rsi; - ctx->uc_mcontext.rbp = cr->Rbp; - ctx->uc_mcontext.rsp = cr->Rsp; - ctx->uc_mcontext.rip = cr->Rip; - ctx->uc_mcontext.r8 = cr->R8; - ctx->uc_mcontext.r9 = cr->R9; - ctx->uc_mcontext.r10 = cr->R10; - ctx->uc_mcontext.r11 = cr->R11; - ctx->uc_mcontext.r12 = cr->R12; - ctx->uc_mcontext.r13 = cr->R13; - ctx->uc_mcontext.r14 = cr->R14; - ctx->uc_mcontext.r15 = cr->R15; - ctx->uc_mcontext.cs = cr->SegCs; - ctx->uc_mcontext.gs = cr->SegGs; - ctx->uc_mcontext.fs = cr->SegFs; - ctx->uc_mcontext.fpregs = &ctx->__fpustate; - __repmovsb(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate)); - ctx->__fpustate.mxcsr = cr->MxCsr; -} - -textwindows void _ntlinux2context(struct NtContext *cr, const ucontext_t *ctx) { - if (!cr) - return; - cr->EFlags = ctx->uc_mcontext.eflags; - cr->Rax = ctx->uc_mcontext.rax; - cr->Rbx = ctx->uc_mcontext.rbx; - cr->Rcx = ctx->uc_mcontext.rcx; - cr->Rdx = ctx->uc_mcontext.rdx; - cr->Rdi = ctx->uc_mcontext.rdi; - cr->Rsi = ctx->uc_mcontext.rsi; - cr->Rbp = ctx->uc_mcontext.rbp; - cr->Rsp = ctx->uc_mcontext.rsp; - cr->Rip = ctx->uc_mcontext.rip; - cr->R8 = ctx->uc_mcontext.r8; - cr->R9 = ctx->uc_mcontext.r9; - cr->R10 = ctx->uc_mcontext.r10; - cr->R11 = ctx->uc_mcontext.r11; - cr->R12 = ctx->uc_mcontext.r12; - cr->R13 = ctx->uc_mcontext.r13; - cr->R14 = ctx->uc_mcontext.r14; - cr->R15 = ctx->uc_mcontext.r15; - cr->SegCs = ctx->uc_mcontext.cs; - cr->SegGs = ctx->uc_mcontext.gs; - cr->SegFs = ctx->uc_mcontext.fs; - cr->MxCsr = ctx->__fpustate.mxcsr; - __repmovsb(&cr->FltSave, &ctx->__fpustate, sizeof(ctx->__fpustate)); -} - -#endif /* __x86_64__ */ diff --git a/libc/intrin/printmaps.c b/libc/intrin/printmaps.c index 0747a50dc07..fbd30d1796a 100644 --- a/libc/intrin/printmaps.c +++ b/libc/intrin/printmaps.c @@ -18,23 +18,57 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/bsr.h" #include "libc/intrin/describeflags.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/maps.h" -#include "libc/macros.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/auxv.h" +// this will usually return 12 since x86 pml4t uses a 47 bit address +// space in userspace, and decent arm machines uses a 48 bit address +// space. however it could go lower on embedded devices. it can also +// rise higher on expensive x86 machines with pml5t, if user uses it +static int get_address_digits(int pagesz) { + int max_bits = 0; + for (struct Tree *e = tree_first(__maps.maps); e; e = tree_next(e)) { + struct Map *map = MAP_TREE_CONTAINER(e); + char *end = map->addr + ((map->size + pagesz - 1) & -pagesz); + int bits = bsrll((uintptr_t)end) + 1; + if (bits > max_bits) + max_bits = bits; + } + return ((max_bits + 3) & -4) / 4; +} + /** - * Prints memory mappings. + * Prints memory mappings known to cosmo. */ void __print_maps(size_t limit) { - char mappingbuf[8], sb[16]; __maps_lock(); + char sb[16]; + char mappingbuf[8]; + struct Map *last = 0; + int pagesz = __pagesize; + int digs = get_address_digits(pagesz); for (struct Tree *e = tree_first(__maps.maps); e; e = tree_next(e)) { struct Map *map = MAP_TREE_CONTAINER(e); - kprintf("%012lx-%012lx %!s", map->addr, map->addr + map->size, + + // show gaps between maps + if (last) { + char *beg = last->addr + ((last->size + pagesz - 1) & -pagesz); + char *end = map->addr; + if (end > beg) { + size_t gap = end - beg; + sizefmt(sb, gap, 1024); + kprintf("%0*lx-%0*lx %sb\n", digs, beg, digs, end, sb); + } + } + last = map; + + // show mapping + kprintf("%0*lx-%0*lx %!s", digs, map->addr, digs, map->addr + map->size, _DescribeMapping(mappingbuf, map->prot, map->flags)); sizefmt(sb, map->size, 1024); kprintf(" %!sb", sb); @@ -45,10 +79,14 @@ void __print_maps(size_t limit) { if (map->readonlyfile) kprintf(" readonlyfile"); kprintf("\n"); + + // stay beneath our limit if (!--limit) break; } - kprintf("# %'zu bytes in %'zu mappings\n", __maps.pages * __pagesize, + + // print summary + kprintf("# %'zu bytes in %'zu mappings\n", __maps.pages * pagesz, __maps.count); __maps_unlock(); } diff --git a/libc/intrin/sig.c b/libc/intrin/sig.c index b49356a5398..d25429de655 100644 --- a/libc/intrin/sig.c +++ b/libc/intrin/sig.c @@ -38,19 +38,25 @@ #include "libc/intrin/nomultics.h" #include "libc/intrin/strace.h" #include "libc/intrin/weaken.h" +#include "libc/log/libfatal.internal.h" +#include "libc/mem/alloca.h" #include "libc/nt/console.h" #include "libc/nt/enum/context.h" #include "libc/nt/enum/exceptionhandleractions.h" +#include "libc/nt/enum/pageflags.h" #include "libc/nt/enum/processcreationflags.h" #include "libc/nt/enum/signal.h" #include "libc/nt/enum/status.h" #include "libc/nt/events.h" +#include "libc/nt/memory.h" #include "libc/nt/runtime.h" #include "libc/nt/signals.h" +#include "libc/nt/struct/memorybasicinformation.h" #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/runtime/internal.h" +#include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" #include "libc/sysv/consts/map.h" @@ -67,6 +73,7 @@ */ #define STKSZ 65536 +#define HAIRY textwindows dontinstrument dontinline struct SignalFrame { unsigned rva; @@ -75,16 +82,21 @@ struct SignalFrame { ucontext_t ctx; }; +__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; +__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; +__msabi extern typeof(VirtualQuery) *const __imp_VirtualQuery; +__msabi extern typeof(WriteFile) *const __imp_WriteFile; + extern pthread_mutex_t __sig_worker_lock; -static textwindows bool __sig_ignored_by_default(int sig) { +HAIRY static bool __sig_ignored_by_default(int sig) { return sig == SIGURG || // sig == SIGCONT || // sig == SIGCHLD || // sig == SIGWINCH; } -textwindows bool __sig_ignored(int sig) { +HAIRY bool __sig_ignored(int sig) { return __sighandrvas[sig] == (intptr_t)SIG_IGN || (__sighandrvas[sig] == (intptr_t)SIG_DFL && __sig_ignored_by_default(sig)); @@ -101,7 +113,7 @@ textwindows void __sig_delete(int sig) { _pthread_unlock(); } -static textwindows int __sig_getter(atomic_ulong *sigs, sigset_t masked) { +textwindows static int __sig_getter(atomic_ulong *sigs, sigset_t masked) { int sig; sigset_t bit, pending, deliverable; for (;;) { @@ -124,8 +136,8 @@ textwindows int __sig_get(sigset_t masked) { return sig; } -static textwindows bool __sig_should_use_altstack(unsigned flags, - struct CosmoTib *tib) { +HAIRY static bool __sig_should_use_altstack(unsigned flags, + struct CosmoTib *tib) { if (!(flags & SA_ONSTACK)) return false; // signal handler didn't enable it if (!tib->tib_sigstack_size) @@ -139,7 +151,7 @@ static textwindows bool __sig_should_use_altstack(unsigned flags, return true; } -static textwindows wontreturn void __sig_terminate(int sig) { +forceinline wontreturn void __sig_terminate(int sig) { TerminateThisProcess(sig); } @@ -242,7 +254,8 @@ textwindows int __sig_raise(volatile int sig, int sic) { // loop back to top // jump where handler says sig = 0; - return setcontext(&ctx); + setcontext(&ctx); + __builtin_unreachable(); } textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) { @@ -256,7 +269,7 @@ textwindows int __sig_relay(int sig, int sic, sigset_t waitmask) { } // the user's signal handler callback is wrapped with this trampoline -static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) { +textwindows wontreturn static void __sig_tramp(struct SignalFrame *sf) { int sig = sf->si.si_signo; struct CosmoTib *tib = __get_tls(); struct PosixThread *pt = (struct PosixThread *)tib->tib_pthread; @@ -298,8 +311,36 @@ static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) { } } +HAIRY optimizespeed void __sig_translate(ucontext_t *ctx, + const struct NtContext *cr) { + ctx->uc_mcontext.eflags = cr->EFlags; + ctx->uc_mcontext.rax = cr->Rax; + ctx->uc_mcontext.rbx = cr->Rbx; + ctx->uc_mcontext.rcx = cr->Rcx; + ctx->uc_mcontext.rdx = cr->Rdx; + ctx->uc_mcontext.rdi = cr->Rdi; + ctx->uc_mcontext.rsi = cr->Rsi; + ctx->uc_mcontext.rbp = cr->Rbp; + ctx->uc_mcontext.rsp = cr->Rsp; + ctx->uc_mcontext.rip = cr->Rip; + ctx->uc_mcontext.r8 = cr->R8; + ctx->uc_mcontext.r9 = cr->R9; + ctx->uc_mcontext.r10 = cr->R10; + ctx->uc_mcontext.r11 = cr->R11; + ctx->uc_mcontext.r12 = cr->R12; + ctx->uc_mcontext.r13 = cr->R13; + ctx->uc_mcontext.r14 = cr->R14; + ctx->uc_mcontext.r15 = cr->R15; + ctx->uc_mcontext.cs = cr->SegCs; + ctx->uc_mcontext.gs = cr->SegGs; + ctx->uc_mcontext.fs = cr->SegFs; + ctx->uc_mcontext.fpregs = &ctx->__fpustate; + __repmovsb(&ctx->__fpustate, &cr->FltSave, sizeof(ctx->__fpustate)); + ctx->__fpustate.mxcsr = cr->MxCsr; +} + // sends signal to another specific thread which is ref'd -static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { +textwindows static int __sig_killer(struct PosixThread *pt, int sig, int sic) { unsigned rva = __sighandrvas[sig]; unsigned flags = __sighandflags[sig]; @@ -408,8 +449,8 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { sp -= sizeof(struct SignalFrame); sp &= -16; struct SignalFrame *sf = (struct SignalFrame *)sp; - _ntcontext2linux(&sf->ctx, &nc); - bzero(&sf->si, sizeof(sf->si)); + __repstosb(sf, 0, sizeof(*sf)); + __sig_translate(&sf->ctx, &nc); sf->rva = rva; sf->flags = flags; sf->si.si_code = sic; @@ -493,31 +534,46 @@ textwindows void __sig_generate(int sig, int sic) { } } -static textwindows char *__sig_stpcpy(char *d, const char *s) { +HAIRY static char *__sig_stpcpy(char *d, const char *s) { size_t i; for (i = 0;; ++i) if (!(d[i] = s[i])) return d + i; } -static textwindows wontreturn void __sig_death(int sig, const char *thing) { +HAIRY wontreturn static void __sig_death(int sig, const char *thing) { #ifndef TINY intptr_t hStderr; char sigbuf[21], s[128], *p; - hStderr = GetStdHandle(kNtStdErrorHandle); + hStderr = __imp_GetStdHandle(kNtStdErrorHandle); p = __sig_stpcpy(s, "Terminating on "); p = __sig_stpcpy(p, thing); p = __sig_stpcpy(p, strsignal_r(sig, sigbuf)); p = __sig_stpcpy(p, ". Pass --strace and/or ShowCrashReports() for details.\n"); - WriteFile(hStderr, s, p - s, 0, 0); + __imp_WriteFile(hStderr, s, p - s, 0, 0); #endif __sig_terminate(sig); } -static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep, - int code, int sig, - struct CosmoTib *tib) { +// +// "If a program attempts to access an address within a guard page, +// the system raises a kNtStatusGuardPageViolation (0x80000001) +// exception. The system also clears the kNtPageGuard modifier, +// removing the memory page's guard page status. The system will not +// stop the next attempt to access the memory page with a +// kNtStatusGuardPageViolation exception." +// +// —Quoth MSDN § Creating Guard Pages +// +forceinline void __sig_reguard(void *page) { + uint32_t old_protect; + __imp_VirtualProtect((void *)((uintptr_t)page & -__pagesize), __pagesize, + kNtPageReadwrite | kNtPageGuard, &old_protect); +} + +// trampoline for calling signal handler when system reports crash +textwindows static void __sig_unmaskable(struct SignalFrame *sf) { // log vital crash information reliably for --strace before doing much // we don't print this without the flag since raw numbers scare people @@ -525,96 +581,98 @@ static textwindows void __sig_unmaskable(struct NtExceptionPointers *ep, // 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, + sf->si.si_errno, sf->si.si_signo, _weaken(FindDebugBinary) ? _weaken(FindDebugBinary)() : program_invocation_name, - ep->ContextRecord->Rip, - DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp)); - - // 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]; - if (rva == (intptr_t)SIG_DFL || rva == (intptr_t)SIG_IGN) - __sig_death(sig, "uncaught "); - - // 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; - } + sf->ctx.uc_mcontext.gregs[REG_RIP], + DescribeBacktrace( + (struct StackFrame *)sf->ctx.uc_mcontext.gregs[REG_RBP])); - // 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; - } + // this will restore the guard page if the user is using a sigaltstack + if (sf->si.si_errno == kNtStatusGuardPageViolation) + __sig_reguard(sf->si.si_addr); // call the user signal handler // and a modifiable view of the faulting code's cpu state - // temporarily replace signal mask while calling crash handler - // abort process if sig is already blocked to avoid crash loop - // 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); - sigset_t blocksigs = __sighandmask[sig]; - if (!(flags & SA_NODEFER)) - blocksigs |= 1ull << (sig - 1); - ctx.uc_sigmask = atomic_fetch_or_explicit(&tib->tib_sigmask, blocksigs, - memory_order_acquire); - if (ctx.uc_sigmask & (1ull << (sig - 1))) { - __sig_death(sig, "masked "); - __sig_terminate(sig); - } - __sig_handler(rva)(sig, &si, &ctx); - atomic_store_explicit(&tib->tib_sigmask, ctx.uc_sigmask, + // then finally restore signal mask and return control to program + __sig_handler(sf->rva)(sf->si.si_signo, &sf->si, &sf->ctx); + atomic_store_explicit(&__get_tls()->tib_sigmask, sf->ctx.uc_sigmask, memory_order_release); - _ntlinux2context(ep->ContextRecord, &ctx); + setcontext(&sf->ctx); + __builtin_unreachable(); } -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) { +__msabi HAIRY static unsigned __sig_crash(struct NtExceptionPointers *ep) { - // translate win32 to unix si_signo and si_code - int code, sig = __sig_crash_sig(ep->ExceptionRecord->ExceptionCode, &code); + // translate the win32 exception code into unix's si_signo and si_code + int sic, sig = __sig_crash_sig(ep->ExceptionRecord->ExceptionCode, &sic); - // advance the instruction pointer to skip over debugger breakpoints - // this behavior is consistent with how unix kernels are implemented - if (sig == SIGTRAP) { + // advances the instruction pointer, to skip over debugger breakpoints + // this makes windows 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(); + // clears signal handler if user asked sigaction for one-shot behavior + unsigned rva = __sighandrvas[sig]; unsigned flags = __sighandflags[sig]; + if (flags & SA_RESETHAND) + __sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL; + + // kills process if the user did not specify a handler for this signal + // we also don't allow unmaskable signals to be ignored by the program + if (rva == (intptr_t)SIG_DFL || // + rva == (intptr_t)SIG_IGN) + __sig_death(sig, "uncaught "); + + // we kill the process if this thread's signal mask blocks this signal + // then we block some extra signals while executing the signal handler + struct CosmoTib *tib = __get_tls(); + sigset_t blocksigs = __sighandmask[sig]; + if (!(flags & SA_NODEFER)) + blocksigs |= 1ull << (sig - 1); + sigset_t oldsigmask = atomic_fetch_or(&tib->tib_sigmask, blocksigs); + if (oldsigmask & (1ull << (sig - 1))) + __sig_death(sig, "masked "); + + // we don't know if it is safe for signal handlers to longjmp() out of + // win32 vectored exception handlers so let's copy the machine context + // and tell win32 to restore control to __sig_unmaskable() which shall + // call the user signal handler safely. please note that if this crash + // was caused by stack overflow, then we're literally executing inside + // the guard page so this code can't use more than 4096 bytes of stack + uintptr_t sp; if (__sig_should_use_altstack(flags, tib)) { - __stack_call(ep, code, sig, tib, __sig_unmaskable, - tib->tib_sigstack_addr + tib->tib_sigstack_size); + sp = (uintptr_t)tib->tib_sigstack_addr + tib->tib_sigstack_size; } else { - __sig_unmaskable(ep, code, sig, tib); + size_t n = sizeof(struct SignalFrame) + 32; + sp = (uintptr_t)alloca(n) + n; } - - // resume running user program - // hopefully the user fixed the cpu state - // otherwise the crash will keep happening + sp -= sizeof(struct SignalFrame); + sp &= -16; + struct SignalFrame *sf = (struct SignalFrame *)sp; + __repstosb(sf, 0, sizeof(*sf)); + __sig_translate(&sf->ctx, ep->ContextRecord); + sf->ctx.uc_sigmask = oldsigmask; + sf->rva = rva; + sf->flags = flags; + sf->si.si_code = sic; + sf->si.si_signo = sig; + sf->si.si_errno = ep->ExceptionRecord->ExceptionCode; + if (sf->si.si_errno == kNtStatusGuardPageViolation) { + sf->si.si_addr = (void *)ep->ExceptionRecord->ExceptionInformation[1]; + } else { + sf->si.si_addr = ep->ExceptionRecord->ExceptionAddress; + } + *(uintptr_t *)(sp -= sizeof(uintptr_t)) = ep->ContextRecord->Rip; + ep->ContextRecord->Rip = (intptr_t)__sig_unmaskable; + ep->ContextRecord->Rdi = (intptr_t)sf; + ep->ContextRecord->Rsp = sp; return kNtExceptionContinueExecution; } -static textwindows int __sig_console_sig(uint32_t dwCtrlType) { +textwindows static int __sig_console_sig(uint32_t dwCtrlType) { switch (dwCtrlType) { case kNtCtrlCEvent: return SIGINT; @@ -629,7 +687,7 @@ static textwindows int __sig_console_sig(uint32_t dwCtrlType) { } } -static textwindows int __sig_console_char(uint32_t dwCtrlType) { +textwindows static int __sig_console_char(uint32_t dwCtrlType) { switch (dwCtrlType) { case kNtCtrlCEvent: return __ttyconf.vintr; @@ -640,7 +698,7 @@ static textwindows int __sig_console_char(uint32_t dwCtrlType) { } } -__msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) { +__msabi HAIRY bool32 __sig_console(uint32_t dwCtrlType) { // win32 launches a thread to deliver ctrl-c and ctrl-break when typed // it only happens when kNtEnableProcessedInput is in play on console. // otherwise we need to wait until read-nt.c discovers that keystroke. @@ -677,7 +735,7 @@ textwindows int __sig_check(void) { // the process was tuned to have more fine-grained event timing. we want // signals to happen faster when possible; that happens when cancelation // points, e.g. read need to wait on i/o; they too check for new signals -textwindows dontinstrument static uint32_t __sig_worker(void *arg) { +HAIRY static uint32_t __sig_worker(void *arg) { struct CosmoTib tls; __bootstrap_tls(&tls, __builtin_frame_address(0)); char *sp = __builtin_frame_address(0); diff --git a/libc/intrin/stack.c b/libc/intrin/stack.c index 9a1e666450b..8b061853bd9 100644 --- a/libc/intrin/stack.c +++ b/libc/intrin/stack.c @@ -31,6 +31,8 @@ #include "libc/intrin/rlimit.h" #include "libc/intrin/strace.h" #include "libc/intrin/weaken.h" +#include "libc/limits.h" +#include "libc/macros.h" #include "libc/runtime/runtime.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/map.h" @@ -118,7 +120,7 @@ static void *flixmap(void *addr, size_t size, int prot, int flags) { static void *slackmap(size_t stacksize, size_t guardsize) { int olde = errno; struct Map *prev, *map; - char *max = (char *)0x7fffffffffff; + char *max = (char *)PTRDIFF_MAX; size_t need = guardsize + stacksize; __maps_lock(); for (;;) { @@ -126,9 +128,9 @@ static void *slackmap(size_t stacksize, size_t guardsize) { // look for empty space beneath higher mappings char *region = 0; for (map = __maps_floor(max); map; map = prev) { - char *min = (char *)(intptr_t)__pagesize; + char *min = (char *)(intptr_t)__gransize; if ((prev = __maps_prev(map))) - min = prev->addr + prev->size; + min = prev->addr + ROUNDUP(prev->size, __gransize); if (map->addr - min >= need) { region = map->addr - need; max = region - 1; @@ -356,7 +358,7 @@ void cosmo_stack_setmaxstacks(int maxstacks) { */ errno_t cosmo_stack_alloc(size_t *inout_stacksize, // size_t *inout_guardsize, // - void **out_addr) { + void **out_stackaddr) { // validate arguments size_t stacksize = *inout_stacksize; @@ -423,7 +425,7 @@ errno_t cosmo_stack_alloc(size_t *inout_stacksize, // // return stack *inout_stacksize = stacksize; *inout_guardsize = guardsize; - *out_addr = stackaddr; + *out_stackaddr = stackaddr; return 0; } diff --git a/libc/intrin/tailcontext.S b/libc/intrin/tailcontext.S index 071f980673d..8ed1b17c99e 100644 --- a/libc/intrin/tailcontext.S +++ b/libc/intrin/tailcontext.S @@ -57,8 +57,7 @@ __tailcontext: mov 80(%rax),%rsp push 88(%rax) mov 24(%rax),%rdi - - xor %eax,%eax + mov 64(%rax),%rax ret #elif defined(__aarch64__) diff --git a/libc/intrin/ucontext.c b/libc/intrin/ucontext.c index d5ba75a94cc..f7472c1275a 100644 --- a/libc/intrin/ucontext.c +++ b/libc/intrin/ucontext.c @@ -23,7 +23,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/thread/tls.h" -int __tailcontext(const ucontext_t *); +int __tailcontext(const ucontext_t *) wontreturn; /** * Sets machine context. @@ -40,7 +40,7 @@ int setcontext(const ucontext_t *uc) { } else { sys_sigprocmask(SIG_SETMASK, &uc->uc_sigmask, 0); } - return __tailcontext(uc); + __tailcontext(uc); } int __getcontextsig(ucontext_t *uc) { diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index 967a31a2bbd..d0e00b37493 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -25,10 +25,12 @@ #include "libc/intrin/weaken.h" #include "libc/log/backtrace.internal.h" #include "libc/macros.h" +#include "libc/mem/alloca.h" #include "libc/nexgen32e/gc.internal.h" #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" #include "libc/thread/thread.h" @@ -50,9 +52,10 @@ dontinstrument int PrintBacktraceUsingSymbols(int fd, const struct StackFrame *bp, struct SymbolTable *st) { size_t gi; + char *cxxbuf; intptr_t addr; const char *name; - char cxxbuf[3000]; + int cxxbufsize = 0; int i, symbol, addend; struct Garbages *garbage; const struct StackFrame *frame; @@ -91,14 +94,25 @@ dontinstrument int PrintBacktraceUsingSymbols(int fd, symbol = 0; addend = 0; } - if ((name = __get_symbol_name(st, symbol)) && - (_weaken(__is_mangled) && _weaken(__is_mangled)(name))) { - _weaken(__demangle)(cxxbuf, name, sizeof(cxxbuf)); - kprintf("%012lx %lx %s%+d\n", frame, addr, cxxbuf, addend); - name = cxxbuf; - } else { - kprintf("%012lx %lx %s%+d\n", frame, addr, name, addend); + name = __get_symbol_name(st, symbol); +#pragma GCC push_options +#pragma GCC diagnostic ignored "-Walloca-larger-than=" + // decipher c++ symbols if there's enough stack memory + // stack size requirement assumes max_depth's still 20 + if (_weaken(__demangle) && // + _weaken(__is_mangled) && // + _weaken(__is_mangled)(name)) { + if (!cxxbufsize) + if ((cxxbufsize = __get_safe_size(8192, 8192)) >= 512) { + cxxbuf = alloca(cxxbufsize); + CheckLargeStackAllocation(cxxbuf, sizeof(cxxbufsize)); + } + if (cxxbufsize >= 512) + if (_weaken(__demangle)(cxxbuf, name, cxxbufsize) != -1) + name = cxxbuf; } +#pragma GCC pop_options + kprintf("%012lx %lx %s%+d\n", frame, addr, name, addend); } return 0; } diff --git a/libc/log/oncrash_arm64.c b/libc/log/oncrash_arm64.c index 6658a7c84d3..b10d955985c 100644 --- a/libc/log/oncrash_arm64.c +++ b/libc/log/oncrash_arm64.c @@ -396,12 +396,6 @@ relegated void __oncrash(int sig, siginfo_t *si, void *arg) { SpinLock(&lock); __oncrash_impl(sig, si, arg); - // unlike amd64, the instruction pointer on arm64 isn't advanced past - // the debugger breakpoint instruction automatically. we need this so - // execution can resume after __builtin_trap(). - if (arg && sig == SIGTRAP) - ((ucontext_t *)arg)->uc_mcontext.PC += 4; - // ensure execution doesn't resume for anything but SIGTRAP / SIGQUIT if (arg && sig != SIGTRAP && sig != SIGQUIT) { if (!IsXnu()) { diff --git a/libc/nt/enum/status.h b/libc/nt/enum/status.h index ed3dc8ff380..cc11bc96b96 100644 --- a/libc/nt/enum/status.h +++ b/libc/nt/enum/status.h @@ -2,68 +2,68 @@ #define COSMOPOLITAN_LIBC_NT_STATUS_H_ /* high two bits = {success,informational,warning,error} */ -#define kNtStatusSuccess 0x00000000 /* success statuses */ -#define kNtStatusWait0 0x00000000 -#define kNtStatusAbandonedWait0 0x00000080 -#define kNtStatusUserApc 0x000000C0 -#define kNtStatusTimeout 0x00000102 -#define kNtStatusPending 0x00000103 -#define kNtStatusGuardPageViolation 0x80000001 /* warning statuses */ -#define kNtStatusDatatypeMisalignment 0x80000002 -#define kNtStatusBreakpoint 0x80000003 -#define kNtStatusSingleStep 0x80000004 -#define kNtStatusLongjump 0x80000026 -#define kNtStatusUnwindConsolidate 0x80000029 -#define kNtStatusAccessViolation 0xC0000005 /* error statuses */ -#define kNtStatusInPageError 0xC0000006 -#define kNtStatusInvalidHandle 0xC0000008 -#define kNtStatusInvalidParameter 0xC000000D -#define kNtStatusNoMemory 0xC0000017 -#define kNtStatusIllegalInstruction 0xC000001D -#define kNtStatusNoncontinuableException 0xC0000025 -#define kNtStatusInvalidDisposition 0xC0000026 -#define kNtStatusArrayBoundsExceeded 0xC000008C -#define kNtStatusFloatDenormalOperand 0xC000008D -#define kNtStatusFloatDivideByZero 0xC000008E -#define kNtStatusFloatInexactResult 0xC000008F -#define kNtStatusFloatInvalidOperation 0xC0000090 -#define kNtStatusFloatOverflow 0xC0000091 -#define kNtStatusFloatStackCheck 0xC0000092 -#define kNtStatusFloatUnderflow 0xC0000093 -#define kNtStatusIntegerDivideBYZero 0xC0000094 -#define kNtStatusIntegerOverflow 0xC0000095 -#define kNtStatusPrivilegedInstruction 0xC0000096 -#define kNtStatusStackOverflow 0xC00000FD -#define kNtStatusDllNotFound 0xC0000135 -#define kNtStatusOrdinalNotFound 0xC0000138 -#define kNtStatusEntrypointNotFound 0xC0000139 -#define kNtStatusControlCExit 0xC000013A -#define kNtStatusDllInitFailed 0xC0000142 -#define kNtStatusFloatMultipleFaults 0xC00002B4 -#define kNtStatusFloatMultipleTraps 0xC00002B5 -#define kNtStatusRegNatConsumption 0xC00002C9 -#define kNtStatusHeapCorruption 0xC0000374 -#define kNtStatusStackBufferOverrun 0xC0000409 -#define kNtStatusInvalidCruntimeParameter 0xC0000417 -#define kNtStatusAssertionFailure 0xC0000420 -#define kNtStatusEnclaveViolation 0xC00004A2 -#define kNtStatusSegmentNotification 0x40000005 -#define kNtStatusFatalAppExit 0x40000015 -#define kNtStatusNotFound 0xC0000225 -#define kNtStatusCancelled 0xC0000120 +#define kNtStatusSuccess 0x00000000u /* success statuses */ +#define kNtStatusWait0 0x00000000u +#define kNtStatusAbandonedWait0 0x00000080u +#define kNtStatusUserApc 0x000000C0u +#define kNtStatusTimeout 0x00000102u +#define kNtStatusPending 0x00000103u +#define kNtStatusGuardPageViolation 0x80000001u /* warning statuses */ +#define kNtStatusDatatypeMisalignment 0x80000002u +#define kNtStatusBreakpoint 0x80000003u +#define kNtStatusSingleStep 0x80000004u +#define kNtStatusLongjump 0x80000026u +#define kNtStatusUnwindConsolidate 0x80000029u +#define kNtStatusAccessViolation 0xC0000005u /* error statuses */ +#define kNtStatusInPageError 0xC0000006u +#define kNtStatusInvalidHandle 0xC0000008u +#define kNtStatusInvalidParameter 0xC000000Du +#define kNtStatusNoMemory 0xC0000017u +#define kNtStatusIllegalInstruction 0xC000001Du +#define kNtStatusNoncontinuableException 0xC0000025u +#define kNtStatusInvalidDisposition 0xC0000026u +#define kNtStatusArrayBoundsExceeded 0xC000008Cu +#define kNtStatusFloatDenormalOperand 0xC000008Du +#define kNtStatusFloatDivideByZero 0xC000008Eu +#define kNtStatusFloatInexactResult 0xC000008Fu +#define kNtStatusFloatInvalidOperation 0xC0000090u +#define kNtStatusFloatOverflow 0xC0000091u +#define kNtStatusFloatStackCheck 0xC0000092u +#define kNtStatusFloatUnderflow 0xC0000093u +#define kNtStatusIntegerDivideBYZero 0xC0000094u +#define kNtStatusIntegerOverflow 0xC0000095u +#define kNtStatusPrivilegedInstruction 0xC0000096u +#define kNtStatusStackOverflow 0xC00000FDu +#define kNtStatusDllNotFound 0xC0000135u +#define kNtStatusOrdinalNotFound 0xC0000138u +#define kNtStatusEntrypointNotFound 0xC0000139u +#define kNtStatusControlCExit 0xC000013Au +#define kNtStatusDllInitFailed 0xC0000142u +#define kNtStatusFloatMultipleFaults 0xC00002B4u +#define kNtStatusFloatMultipleTraps 0xC00002B5u +#define kNtStatusRegNatConsumption 0xC00002C9u +#define kNtStatusHeapCorruption 0xC0000374u +#define kNtStatusStackBufferOverrun 0xC0000409u +#define kNtStatusInvalidCruntimeParameter 0xC0000417u +#define kNtStatusAssertionFailure 0xC0000420u +#define kNtStatusEnclaveViolation 0xC00004A2u +#define kNtStatusSegmentNotification 0x40000005u +#define kNtStatusFatalAppExit 0x40000015u +#define kNtStatusNotFound 0xC0000225u +#define kNtStatusCancelled 0xC0000120u -#define kNtDbgExceptionHandled 0x00010001 -#define kNtDbgContinue 0x00010002 -#define kNtDbgReplyLater 0x40010001 -#define kNtDbgTerminateThread 0x40010003 -#define kNtDbgTerminateProcess 0x40010004 -#define kNtDbgControlC 0x40010005 -#define kNtDbgPrintexceptionC 0x40010006 -#define kNtDbgRipexception 0x40010007 -#define kNtDbgControlBreak 0x40010008 -#define kNtDbgCommandException 0x40010009 -#define kNtDbgPrintexceptionWideC 0x4001000A -#define kNtDbgExceptionNotHandled 0x80010001 +#define kNtDbgExceptionHandled 0x00010001u +#define kNtDbgContinue 0x00010002u +#define kNtDbgReplyLater 0x40010001u +#define kNtDbgTerminateThread 0x40010003u +#define kNtDbgTerminateProcess 0x40010004u +#define kNtDbgControlC 0x40010005u +#define kNtDbgPrintexceptionC 0x40010006u +#define kNtDbgRipexception 0x40010007u +#define kNtDbgControlBreak 0x40010008u +#define kNtDbgCommandException 0x40010009u +#define kNtDbgPrintexceptionWideC 0x4001000Au +#define kNtDbgExceptionNotHandled 0x80010001u #define kNtStillActive kNtStatusPending #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 3fc35201e40..5847a18f9e6 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -220,7 +220,7 @@ textstartup void __enable_tls(void) { DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hThread, 0, false, kNtDuplicateSameAccess); - atomic_store_explicit(&tib->tib_syshand, hThread, memory_order_relaxed); + atomic_init(&tib->tib_syshand, hThread); } else if (IsXnuSilicon()) { tib->tib_syshand = __syslib->__pthread_self(); } @@ -233,23 +233,22 @@ textstartup void __enable_tls(void) { } else { tid = sys_gettid(); } - atomic_store_explicit(&tib->tib_tid, tid, memory_order_relaxed); + atomic_init(&tib->tib_tid, tid); // TODO(jart): set_tid_address? // inherit signal mask - if (IsWindows()) { - atomic_store_explicit(&tib->tib_sigmask, - ParseMask(__getenv(environ, "_MASK").s), - memory_order_relaxed); - } + if (IsWindows()) + atomic_init(&tib->tib_sigmask, ParseMask(__getenv(environ, "_MASK").s)); // initialize posix threads _pthread_static.tib = tib; _pthread_static.pt_flags = PT_STATIC; _pthread_static.pt_locale = &__global_locale; + _pthread_static.pt_attr.__stackaddr = __maps.stack.addr; + _pthread_static.pt_attr.__stacksize = __maps.stack.size; dll_init(&_pthread_static.list); _pthread_list = &_pthread_static.list; - atomic_store_explicit(&_pthread_static.ptid, tid, memory_order_release); + atomic_init(&_pthread_static.ptid, tid); // ask the operating system to change the x86 segment register if (IsWindows()) diff --git a/libc/runtime/opensymboltable.greg.c b/libc/runtime/opensymboltable.greg.c index 5da44f023b0..145c8be2190 100644 --- a/libc/runtime/opensymboltable.greg.c +++ b/libc/runtime/opensymboltable.greg.c @@ -130,7 +130,8 @@ static struct SymbolTable *OpenSymbolTableImpl(const char *filename) { ++j; } t->count = j; - munmap(stp, sizeof(const Elf64_Sym *) * n); + if (!IsWindows()) + munmap(stp, sizeof(const Elf64_Sym *) * n); munmap(map, filesize); close(fd); return t; @@ -144,9 +145,8 @@ static struct SymbolTable *OpenSymbolTableImpl(const char *filename) { errno = ENOEXEC; SystemError: STRACE("OpenSymbolTable()% m"); - if (map != MAP_FAILED) { + if (map != MAP_FAILED) munmap(map, filesize); - } close(fd); return 0; } diff --git a/libc/runtime/sigsetjmp.S b/libc/runtime/sigsetjmp.S index 1ef838ccef2..3187bd2956f 100644 --- a/libc/runtime/sigsetjmp.S +++ b/libc/runtime/sigsetjmp.S @@ -29,7 +29,7 @@ // Saves caller CPU state and signal mask. // -// @param rdi points to jmp_buf +// @param rdi points to sigjmp_buf // @param esi if non-zero will cause mask to be saved // @return eax 0 when set and !0 when longjmp'd // @returnstwice diff --git a/libc/runtime/sysconf.c b/libc/runtime/sysconf.c index 17ff15c548d..4f3dafd755b 100644 --- a/libc/runtime/sysconf.c +++ b/libc/runtime/sysconf.c @@ -45,8 +45,8 @@ * - `_SC_GRANSIZE` returns addr alignment for mmap() * - `_SC_CLK_TCK` returns number of clock ticks per second * - `_SC_ARG_MAX` will perform expensive rlimit calculations - * - `_SC_SIGSTKSZ` returns host platform's preferred SIGSTKSZ - * - `_SC_MINSIGSTKSZ` returns host platform's required MINSIGSTKSZ + * - `_SC_SIGSTKSZ` returns recommended `SIGSTKSZ` for platform + * - `_SC_MINSIGSTKSZ` returns size of kernel pushed signal frame * - `_SC_AVPHYS_PAGES` returns average physical memory pages * - `_SC_PHYS_PAGES` returns physical memory pages available * - `_SC_NPROCESSORS_ONLN` returns number of effective CPUs @@ -67,7 +67,7 @@ long sysconf(int name) { case _SC_ARG_MAX: return __get_arg_max(); case _SC_SIGSTKSZ: - return _SIGSTKSZ; + return __get_minsigstksz() + SIGSTKSZ; case _SC_MINSIGSTKSZ: return __get_minsigstksz(); case _SC_CHILD_MAX: diff --git a/libc/runtime/zipos-get.c b/libc/runtime/zipos-get.c index 55660e28547..e98b6b3635f 100644 --- a/libc/runtime/zipos-get.c +++ b/libc/runtime/zipos-get.c @@ -63,11 +63,9 @@ static void __zipos_dismiss(uint8_t *map, const uint8_t *cdir, long pg) { } // unmap the executable portion beneath the local files - if (!IsWindows()) { - mo = ROUNDDOWN(lo, __gransize); - if (mo) - munmap(map, mo); - } + mo = ROUNDDOWN(lo, __gransize); + if (mo && !IsWindows()) + munmap(map, mo); } static int __zipos_compare_names(const void *a, const void *b, void *c) { diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index e09d248703c..48742fc3f1c 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -1104,8 +1104,8 @@ syscon limits _ARG_MAX 128*1024 128*1024 1024*1024 1024*1024 512*1024 51 syscon limits _NAME_MAX 255 255 255 255 255 255 511 255 # probably higher on windows? syscon limits _PATH_MAX 4096 4096 1024 1024 1024 1024 1024 260 # syscon limits _NSIG 64 64 32 32 128 32 64 64 # _SIG_MAXSIG on FreeBSD -syscon limits _MINSIGSTKSZ 2048 2048 32768 32768 4096 12288 8192 2048 # -syscon limits _SIGSTKSZ 8192 2048 131072 131072 36864 28672 40960 8192 # +syscon limits _MINSIGSTKSZ 2048 6144 8192 32768 6656 14336 8192 2048 # FreeBSD upscaled a bit for ARM +syscon limits _SIGSTKSZ 10240 10240 131072 131072 36864 28672 40960 10240 # # unmount() flags # a.k.a. umount2() on linux diff --git a/libc/sysv/consts/_MINSIGSTKSZ.S b/libc/sysv/consts/_MINSIGSTKSZ.S index b55cbcca924..94d7efe537e 100644 --- a/libc/sysv/consts/_MINSIGSTKSZ.S +++ b/libc/sysv/consts/_MINSIGSTKSZ.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon limits,_MINSIGSTKSZ,2048,2048,32768,32768,4096,12288,8192,2048 +.syscon limits,_MINSIGSTKSZ,2048,6144,8192,32768,6656,14336,8192,2048 diff --git a/libc/sysv/consts/_SIGSTKSZ.S b/libc/sysv/consts/_SIGSTKSZ.S index 6347f287744..8484e659655 100644 --- a/libc/sysv/consts/_SIGSTKSZ.S +++ b/libc/sysv/consts/_SIGSTKSZ.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon limits,_SIGSTKSZ,8192,2048,131072,131072,36864,28672,40960,8192 +.syscon limits,_SIGSTKSZ,10240,10240,131072,131072,36864,28672,40960,10240 diff --git a/libc/sysv/consts/sig.h b/libc/sysv/consts/sig.h index 3a66c711b95..d0c6808d29e 100644 --- a/libc/sysv/consts/sig.h +++ b/libc/sysv/consts/sig.h @@ -2,45 +2,25 @@ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_SIG_H_ COSMOPOLITAN_C_START_ -extern const int SIGABRT; -extern const int SIGALRM; extern const int SIGBUS; extern const int SIGTHR; extern const int SIGCHLD; extern const int SIGCONT; extern const int SIGEMT; -extern const int SIGFPE; -extern const int SIGHUP; -extern const int SIGILL; extern const int SIGINFO; -extern const int SIGINT; extern const int SIGIO; -extern const int SIGIOT; -extern const int SIGKILL; -extern const int SIGPIPE; extern const int SIGPOLL; -extern const int SIGPROF; extern const int SIGPWR; -extern const int SIGQUIT; extern const int SIGRTMAX; extern const int SIGRTMIN; -extern const int SIGSEGV; extern const int SIGSTKFLT; extern const int SIGSTOP; extern const int SIGSYS; -extern const int SIGTERM; -extern const int SIGTRAP; extern const int SIGTSTP; -extern const int SIGTTIN; -extern const int SIGTTOU; extern const int SIGUNUSED; extern const int SIGURG; extern const int SIGUSR1; extern const int SIGUSR2; -extern const int SIGVTALRM; -extern const int SIGWINCH; -extern const int SIGXCPU; -extern const int SIGXFSZ; extern const int SIG_BLOCK; extern const int SIG_SETMASK; diff --git a/libc/sysv/consts/ss.h b/libc/sysv/consts/ss.h index ef83ec6ec3e..626a696d6da 100644 --- a/libc/sysv/consts/ss.h +++ b/libc/sysv/consts/ss.h @@ -8,7 +8,7 @@ extern const int _MINSIGSTKSZ; COSMOPOLITAN_C_END_ -#define SIGSTKSZ 32768 +#define SIGSTKSZ 32768 /* just itself believed to be safe */ #define MINSIGSTKSZ 32768 /* xnu defines the highest minimum */ #define SS_ONSTACK 1 #define SS_DISABLE SS_DISABLE diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index e496b4f3cba..aaa74a6ede1 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -38,9 +38,12 @@ #include "libc/nexgen32e/nexgen32e.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" +#include "libc/stdio/rand.h" #include "libc/str/str.h" #include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rlimit.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/aspect.internal.h" @@ -95,14 +98,24 @@ int main(int argc, char *argv[]) { struct Dll *e; struct TestAspect *a; + // some settings + __ubsan_strict = true; + __log_level = kLogInfo; + if (errno) { tinyprint(2, "error: the errno variable was contaminated by constructors\n", NULL); return 1; } - __ubsan_strict = true; - __log_level = kLogInfo; + // test huge pointers by enabling pml5t + if (_rand64() % 2) { + errno_t e = errno; + mmap((char *)0x80000000000000, 1, PROT_NONE, // + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + errno = e; + } + GetOpts(argc, argv); for (fd = 3; fd < 100; ++fd) { diff --git a/libc/thread/pthread_attr_setstacksize.c b/libc/thread/pthread_attr_setstacksize.c index 7b7eed9dad5..217b62fadf9 100644 --- a/libc/thread/pthread_attr_setstacksize.c +++ b/libc/thread/pthread_attr_setstacksize.c @@ -20,7 +20,15 @@ #include "libc/thread/thread.h" /** - * Specifies minimum stack size for thread. + * Specifies minimum stack size for thread, e.g. + * + * pthread_t th; + * pthread_attr_t attr; + * pthread_attr_init(&attr); + * pthread_attr_setguardsize(&attr, 4096); + * pthread_attr_setstacksize(&attr, 61440); + * pthread_create(&th, &attr, thfunc, arg); + * pthread_attr_destroy(&attr); * * On Linux, if you're not using `cosmocc -mtiny`, and you're not using * cosmo_dlopen(), and guard size is nonzero, then `MAP_GROWSDOWN` will diff --git a/libc/thread/pthread_getattr_np.c b/libc/thread/pthread_getattr_np.c index 7742b329ff8..a5747214969 100644 --- a/libc/thread/pthread_getattr_np.c +++ b/libc/thread/pthread_getattr_np.c @@ -16,18 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/calls/struct/rlimit.h" -#include "libc/dce.h" -#include "libc/intrin/atomic.h" -#include "libc/intrin/maps.h" -#include "libc/limits.h" -#include "libc/macros.h" -#include "libc/runtime/runtime.h" #include "libc/str/str.h" -#include "libc/sysv/consts/auxv.h" -#include "libc/sysv/consts/rlim.h" -#include "libc/sysv/consts/rlimit.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" @@ -72,10 +61,5 @@ errno_t pthread_getattr_np(pthread_t thread, pthread_attr_t *attr) { default: __builtin_unreachable(); } - if (!attr->__stacksize && (pt->pt_flags & PT_STATIC)) { - attr->__stackaddr = __maps.stack.addr; - attr->__stacksize = __maps.stack.size; - attr->__guardsize = 0; - } return 0; } diff --git a/test/libc/calls/getcontext_test.c b/test/libc/calls/getcontext_test.c index 35a9db833fe..c80219c7fb0 100644 --- a/test/libc/calls/getcontext_test.c +++ b/test/libc/calls/getcontext_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/ucontext.internal.h" #include "libc/calls/ucontext.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sig.h" @@ -60,6 +61,7 @@ TEST(getcontext, canReadAndWriteSignalMask) { ASSERT_EQ(0, getcontext(&context)); if (!n) { n = 1; + context.uc_mcontext.RES0 = 0; ASSERT_TRUE(sigismember(&context.uc_sigmask, SIGUSR1)); sigaddset(&context.uc_sigmask, SIGUSR2); setcontext(&context); diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index 9206016e2e2..b856b7b4901 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -400,15 +400,16 @@ TEST(sigaction, ignoreSigSegv_notPossible) { _Exit(pSegfault(0)); TERMS(SIGSEGV); } +#endif +#if 0 +// TODO(jart): Use sigsuspend() to make not flaky. TEST(sigaction, killSigSegv_canBeIgnored) { int child, ws; - if (IsWindows()) return; // TODO sighandler_t old = signal(SIGSEGV, SIG_IGN); ASSERT_NE(-1, (child = fork())); - while (!child) { + while (!child) pause(); - } ASSERT_SYS(0, 0, kill(child, SIGSEGV)); EXPECT_SYS(0, 0, kill(child, SIGTERM)); EXPECT_SYS(0, child, wait(&ws)); diff --git a/test/libc/calls/sigaltstack_test.c b/test/libc/calls/sigaltstack_test.c index c2ed85c42c9..3c1d63baccb 100644 --- a/test/libc/calls/sigaltstack_test.c +++ b/test/libc/calls/sigaltstack_test.c @@ -19,6 +19,9 @@ #include "libc/calls/struct/sigaltstack.h" #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/mem/gc.h" +#include "libc/mem/mem.h" +#include "libc/runtime/sysconf.h" #include "libc/sysv/consts/ss.h" #include "libc/testlib/testlib.h" @@ -38,3 +41,13 @@ TEST(sigaltstack, disable) { EXPECT_SYS(0, 0, sigaltstack(0, &ss)); EXPECT_EQ(SS_DISABLE, ss.ss_flags); } + +TEST(sigaltstack, size_requirement) { + struct sigaltstack ss; + EXPECT_SYS(0, 0, sigaltstack(0, &ss)); + ss.ss_size = sysconf(_SC_MINSIGSTKSZ); + ss.ss_sp = gc(malloc(ss.ss_size)); + ss.ss_flags = 0; + ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); + ASSERT_SYS(0, 0, sigaltstack(0, &ss)); +} diff --git a/test/libc/calls/stackoverflow1_test.c b/test/libc/calls/stackoverflow1_test.c index e2dfa79f2c8..6f1e2a32b2b 100644 --- a/test/libc/calls/stackoverflow1_test.c +++ b/test/libc/calls/stackoverflow1_test.c @@ -16,10 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/atomic.h" #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/calls/struct/ucontext.internal.h" +#include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" #include "libc/limits.h" @@ -27,12 +31,15 @@ #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/runtime/sysconf.h" +#include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" #include "libc/stdio/sysparam.h" +#include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/prot.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" /** @@ -42,15 +49,17 @@ */ jmp_buf recover; -volatile bool smashed_stack; +atomic_bool g_isdone; +atomic_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); + unassert(!sigaltstack(0, &ss)); + unassert(SS_ONSTACK == ss.ss_flags); + kprintf("kprintf avoids overflowing %G si_addr=%lx sp=%lx\n", si->si_signo, + si->si_addr, ((ucontext_t *)ctx)->uc_mcontext.SP); smashed_stack = true; - ASSERT_TRUE(__is_stack_overflow(si, ctx)); + unassert(__is_stack_overflow(si, ctx)); longjmp(recover, 123); } @@ -63,7 +72,7 @@ void SetUp(void) { struct rlimit rl; getrlimit(RLIMIT_STACK, &rl); rl.rlim_cur = MIN(rl.rlim_cur, 2 * 1024 * 1024); - ASSERT_SYS(0, 0, setrlimit(RLIMIT_STACK, &rl)); + unassert(!setrlimit(RLIMIT_STACK, &rl)); } // set up the signal handler and alternative stack @@ -72,7 +81,7 @@ void SetUp(void) { 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)); + unassert(!sigaltstack(&ss, 0)); sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important sigemptyset(&sa.sa_mask); sa.sa_sigaction = CrashHandler; @@ -89,20 +98,39 @@ int StackOverflow(int d) { return 0; } -TEST(stackoverflow, standardStack_altStack_process_longjmp) { +void *innocent_thread(void *arg) { + atomic_long dont_clobber_me_bro = 0; + while (!g_isdone) + unassert(!dont_clobber_me_bro); + return 0; +} + +int main() { + + // libc/intrin/stack.c is designed so that this thread's stack should + // be allocated right beneath the main thread's stack. our goal is to + // make sure overflowing the main stack won't clobber our poor thread + pthread_t th; + unassert(!pthread_create(&th, 0, innocent_thread, 0)); + + SetUp(); + int jumpcode; - if (!(jumpcode = setjmp(recover))) { - exit(StackOverflow(0)); - } - ASSERT_EQ(123, jumpcode); - ASSERT_TRUE(smashed_stack); + if (!(jumpcode = setjmp(recover))) + exit(StackOverflow(1)); + unassert(123 == jumpcode); + unassert(smashed_stack); + + // join the thread + g_isdone = true; + unassert(!pthread_join(th, 0)); // here's where longjmp() gets us into trouble struct sigaltstack ss; - ASSERT_SYS(0, 0, sigaltstack(0, &ss)); + unassert(!sigaltstack(0, &ss)); if (IsXnu() || IsNetbsd()) { - ASSERT_EQ(SS_ONSTACK, ss.ss_flags); // wut + unassert(SS_ONSTACK == ss.ss_flags); // wut } else { - ASSERT_EQ(0, ss.ss_flags); + unassert(0 == ss.ss_flags); } } diff --git a/test/libc/calls/stackoverflow2_test.c b/test/libc/calls/stackoverflow2_test.c index 0afa6b695e4..520d952e892 100644 --- a/test/libc/calls/stackoverflow2_test.c +++ b/test/libc/calls/stackoverflow2_test.c @@ -16,20 +16,26 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.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/cosmo.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/maps.h" #include "libc/limits.h" #include "libc/mem/gc.h" #include "libc/mem/mem.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/sig.h" #include "libc/sysv/consts/ss.h" -#include "libc/testlib/testlib.h" #include "libc/thread/thread.h" /** @@ -38,17 +44,20 @@ * simple but it can upset kernels / libraries */ -jmp_buf recover; -volatile bool smashed_stack; +sigjmp_buf recover; +atomic_bool is_done; +atomic_bool smashed_stack; +atomic_bool clobbered_other_thread; 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); + unassert(!sigaltstack(0, &ss)); + unassert(SS_ONSTACK == ss.ss_flags); + kprintf("kprintf avoids overflowing %G si_addr=%lx sp=%lx\n", si->si_signo, + si->si_addr, ((ucontext_t *)ctx)->uc_mcontext.SP); smashed_stack = true; - ASSERT_TRUE(__is_stack_overflow(si, ctx)); - longjmp(recover, 123); + unassert(__is_stack_overflow(si, ctx)); + siglongjmp(recover, 123); } int StackOverflow(int d) { @@ -65,40 +74,51 @@ void *MyPosixThread(void *arg) { struct sigaction sa, o1, o2; struct sigaltstack ss; ss.ss_flags = 0; - ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 2048; ss.ss_sp = gc(malloc(ss.ss_size)); - ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); + unassert(!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(StackOverflow(0)); - } - ASSERT_EQ(123, jumpcode); + if (!(jumpcode = sigsetjmp(recover, 1))) + exit(StackOverflow(1)); + unassert(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)); + unassert(!sigaltstack(0, &ss)); if (IsXnu() || IsNetbsd()) { - ASSERT_EQ(SS_ONSTACK, ss.ss_flags); // wut + unassert(SS_ONSTACK == ss.ss_flags); // wut } else { - ASSERT_EQ(0, ss.ss_flags); + unassert(!ss.ss_flags); } return 0; } -TEST(stackoverflow, standardStack_altStack_thread_longjmp) { - pthread_t th; +void *InnocentThread(void *arg) { + atomic_long dont_clobber_me_bro = 0; + while (!is_done) + if (dont_clobber_me_bro) + clobbered_other_thread = true; + pthread_exit(0); +} + +int main() { + pthread_t th, in; struct sigaltstack ss; for (int i = 0; i < 2; ++i) { + is_done = false; 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); + unassert(!pthread_create(&th, 0, MyPosixThread, 0)); + unassert(!pthread_create(&in, 0, InnocentThread, 0)); + unassert(!pthread_join(th, 0)); + unassert(smashed_stack); + unassert(!sigaltstack(0, &ss)); + unassert(ss.ss_flags == SS_DISABLE); + unassert(!clobbered_other_thread); + is_done = true; + unassert(!pthread_join(in, 0)); } } diff --git a/test/libc/calls/stackoverflow3_test.c b/test/libc/calls/stackoverflow3_test.c index 81ff8c1f993..b83ebcf25c1 100644 --- a/test/libc/calls/stackoverflow3_test.c +++ b/test/libc/calls/stackoverflow3_test.c @@ -98,7 +98,7 @@ void *MyPosixThread(void *arg) { struct sigaction sa; struct sigaltstack ss; ss.ss_flags = 0; - ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 8192; ss.ss_sp = gc(malloc(ss.ss_size)); ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important @@ -106,7 +106,7 @@ void *MyPosixThread(void *arg) { sa.sa_sigaction = CrashHandler; sigaction(SIGBUS, &sa, 0); sigaction(SIGSEGV, &sa, 0); - exit(StackOverflow(0)); + exit(StackOverflow(1)); return 0; } diff --git a/test/libc/calls/stackoverflow4_test.c b/test/libc/calls/stackoverflow4_test.c index 8266ddda034..54d8e240bf4 100644 --- a/test/libc/calls/stackoverflow4_test.c +++ b/test/libc/calls/stackoverflow4_test.c @@ -58,7 +58,7 @@ void *MyPosixThread(void *arg) { struct sigaction sa; struct sigaltstack ss; ss.ss_flags = 0; - ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 4096; + ss.ss_size = sysconf(_SC_MINSIGSTKSZ) + 1024; ss.ss_sp = gc(malloc(ss.ss_size)); ASSERT_SYS(0, 0, sigaltstack(&ss, 0)); sa.sa_flags = SA_SIGINFO | SA_ONSTACK; // <-- important @@ -66,7 +66,7 @@ void *MyPosixThread(void *arg) { sa.sa_handler = CrashHandler; sigaction(SIGBUS, &sa, 0); sigaction(SIGSEGV, &sa, 0); - exit(StackOverflow(0)); + exit(StackOverflow(1)); return 0; } diff --git a/test/libc/calls/stackoverflow5_test.c b/test/libc/calls/stackoverflow5_test.c index 7a33980456b..2d15845a8ce 100644 --- a/test/libc/calls/stackoverflow5_test.c +++ b/test/libc/calls/stackoverflow5_test.c @@ -44,7 +44,7 @@ int StackOverflow(int d) { } void *MyPosixThread(void *arg) { - exit(StackOverflow(0)); + exit(StackOverflow(1)); return 0; } diff --git a/test/libc/intrin/BUILD.mk b/test/libc/intrin/BUILD.mk index 1072638febf..7a1ce175696 100644 --- a/test/libc/intrin/BUILD.mk +++ b/test/libc/intrin/BUILD.mk @@ -59,6 +59,15 @@ o/$(MODE)/test/libc/intrin/%.dbg: \ $(TEST_LIBC_INTRIN_DEPS) \ o/$(MODE)/test/libc/intrin/%.o \ o/$(MODE)/test/libc/intrin/intrin.pkg \ + $(LIBC_TESTMAIN) \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/libc/intrin/mmap_test.dbg: \ + $(TEST_LIBC_INTRIN_DEPS) \ + o/$(MODE)/test/libc/intrin/mmap_test.o \ + o/$(MODE)/test/libc/intrin/intrin.pkg \ o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \ $(LIBC_TESTMAIN) \ $(CRT) \ diff --git a/test/libc/intrin/mmap_test.c b/test/libc/intrin/mmap_test.c index 9475ccacc45..801cfbc92a6 100644 --- a/test/libc/intrin/mmap_test.c +++ b/test/libc/intrin/mmap_test.c @@ -95,7 +95,7 @@ TEST(mmap, pageBeyondGone) { MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, p); EXPECT_TRUE(testlib_memoryexists(p)); - EXPECT_FALSE(testlib_memoryexists(p + 1)); // b/c kisdangerous + EXPECT_TRUE(testlib_memoryexists(p + 1)); EXPECT_FALSE(testlib_memoryexists(p + pagesz)); ASSERT_EQ(0, munmap(p, 1)); } @@ -184,7 +184,7 @@ TEST(mmap, smallerThanPage_mapsRemainder) { ASSERT_NE(MAP_FAILED, map); EXPECT_TRUE(testlib_memoryexists(map)); EXPECT_TRUE(testlib_pokememory(map + (pagesz - 1))); - EXPECT_TRUE(!testlib_memoryexists(map + (pagesz - 1))); + EXPECT_TRUE(testlib_memoryexists(map + (pagesz - 1))); EXPECT_SYS(0, 0, munmap(map, 1)); EXPECT_FALSE(testlib_memoryexists(map)); EXPECT_FALSE(testlib_memoryexists(map + (pagesz - 1))); diff --git a/test/libc/intrin/mprotect_test.c b/test/libc/intrin/mprotect_test.c index 4c2a6a5c301..a04af31c20f 100644 --- a/test/libc/intrin/mprotect_test.c +++ b/test/libc/intrin/mprotect_test.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/ucontext.h" @@ -108,15 +109,15 @@ void SetUp(void) { .sa_flags = SA_SIGINFO | SA_RESETHAND}; struct sigaction sasegv = {.sa_sigaction = OnSigSegv, .sa_flags = SA_SIGINFO | SA_RESETHAND}; - sigaction(SIGBUS, &sabus, old + 0); - sigaction(SIGSEGV, &sasegv, old + 1); + unassert(!sigaction(SIGBUS, &sabus, old + 0)); + unassert(!sigaction(SIGSEGV, &sasegv, old + 1)); gotbusted = false; gotsegv = false; } void TearDown(void) { - sigaction(SIGBUS, old + 0, 0); - sigaction(SIGSEGV, old + 1, 0); + unassert(!sigaction(SIGBUS, old + 0, 0)); + unassert(!sigaction(SIGSEGV, old + 1, 0)); } TEST(mprotect, testOkMemory) { diff --git a/test/posix/signal_latency_async_test.c b/test/posix/signal_latency_async_test.c index ba738bc9708..20956c1a65b 100644 --- a/test/posix/signal_latency_async_test.c +++ b/test/posix/signal_latency_async_test.c @@ -108,6 +108,10 @@ int compare(const void *a, const void *b) { int main() { + // Probably Qemu's fault + if (IsQemuUser()) + return 0; + // TODO(jart): fix flakes if (IsWindows()) return 0;