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

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