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.
This commit is contained in:
Justine Tunney 2024-12-27 01:03:11 -08:00
parent 36e5861b0c
commit 379cd77078
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
48 changed files with 834 additions and 570 deletions

View file

@ -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;

View file

@ -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) {

View file

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

View file

@ -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;
}

View file

@ -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 *) {

View file

@ -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"

View file

@ -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;
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

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

View file

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

View file

@ -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) {
}
}
void __maps_insert(struct Map *map) {
map->flags &= MAP_TYPE | MAP_ANONYMOUS | MAP_NOFORK;
static int __maps_funge_prot(int prot) {
prot &= ~MAP_FIXED;
prot &= ~MAP_FIXED_NOREPLACE;
return prot;
}
// 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;
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;
// 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 {
errno = ENOMEM;
// 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 {
__maps_untrack(addr, size);
}
}
__maps_free(map);
@ -509,24 +638,14 @@ TryAgain:
}
// 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 @@ TryAgain:
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,

View file

@ -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"

View file

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

View file

@ -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"
/**
* Prints memory mappings.
*/
void __print_maps(size_t limit) {
char mappingbuf[8], sb[16];
__maps_lock();
// 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);
kprintf("%012lx-%012lx %!s", map->addr, map->addr + map->size,
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 known to cosmo.
*/
void __print_maps(size_t limit) {
__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);
// 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();
}

View file

@ -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));
sf->ctx.uc_mcontext.gregs[REG_RIP],
DescribeBacktrace(
(struct StackFrame *)sf->ctx.uc_mcontext.gregs[REG_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;
}
// 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 (__sig_should_use_altstack(flags, tib)) {
__stack_call(ep, code, sig, tib, __sig_unmaskable,
tib->tib_sigstack_addr + tib->tib_sigstack_size);
} else {
__sig_unmaskable(ep, code, sig, tib);
}
if (flags & SA_RESETHAND)
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
// resume running user program
// hopefully the user fixed the cpu state
// otherwise the crash will keep happening
// 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)) {
sp = (uintptr_t)tib->tib_sigstack_addr + tib->tib_sigstack_size;
} else {
size_t n = sizeof(struct SignalFrame) + 32;
sp = (uintptr_t)alloca(n) + n;
}
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);

View file

@ -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;
}

View file

@ -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__)

View file

@ -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) {

View file

@ -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;
}

View file

@ -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()) {

View file

@ -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)

View file

@ -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())

View file

@ -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 @@ RaiseEnoexec:
errno = ENOEXEC;
SystemError:
STRACE("OpenSymbolTable()% m");
if (map != MAP_FAILED) {
if (map != MAP_FAILED)
munmap(map, filesize);
}
close(fd);
return 0;
}

View file

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

View file

@ -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:

View file

@ -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) {

View file

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

View file

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

View file

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

View file

@ -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;

View file

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

View file

@ -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) {

View file

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

View file

@ -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;
}

View file

@ -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);

View file

@ -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));

View file

@ -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));
}

View file

@ -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);
}
}

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -44,7 +44,7 @@ int StackOverflow(int d) {
}
void *MyPosixThread(void *arg) {
exit(StackOverflow(0));
exit(StackOverflow(1));
return 0;
}

View file

@ -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) \

View file

@ -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)));

View file

@ -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) {

View file

@ -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;