Write more runtime tests and fix bugs

This change adds tests for the new memory manager code particularly with
its windows support. Function call tracing now works reliably on Silicon
since our function hooker was missing new Apple self-modifying code APIs

Many tests that were disabled a long time ago on aarch64 are reactivated
by this change, now that arm support is on equal terms with x86. There's
been a lot of places where ftrace could cause deadlocks, which have been
hunted down across all platforms thanks to new tests. A bug in Windows's
kill() function has been identified.
This commit is contained in:
Justine Tunney 2025-01-01 22:25:22 -08:00
parent 0b3c81dd4e
commit f24c854b28
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
45 changed files with 550 additions and 872 deletions

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/proc/ntspawn.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/strace.h"

View file

@ -190,7 +190,9 @@ textstartup void __init_fds(int argc, char **argv, char **envp) {
map->prot = PROT_READ | PROT_WRITE;
map->flags = MAP_SHARED | MAP_ANONYMOUS;
map->hand = shand;
__maps_lock();
__maps_insert(map);
__maps_unlock();
}
}
}

View file

@ -112,6 +112,13 @@ void __maps_init(void) {
}
bool __maps_held(void) {
return !__tls_enabled || (__get_tls()->tib_flags & TIB_FLAG_VFORKED) ||
MUTEX_OWNER(
atomic_load_explicit(&__maps.lock.word, memory_order_relaxed)) ==
atomic_load_explicit(&__get_tls()->tib_ptid, memory_order_relaxed);
}
bool __maps_reentrant(void) {
return __tls_enabled && !(__get_tls()->tib_flags & TIB_FLAG_VFORKED) &&
MUTEX_OWNER(
atomic_load_explicit(&__maps.lock.word, memory_order_relaxed)) ==

View file

@ -84,6 +84,7 @@ void __maps_init(void);
void __maps_lock(void);
void __maps_check(void);
void __maps_unlock(void);
bool __maps_reentrant(void);
void *__maps_randaddr(void);
void __maps_add(struct Map *);
void __maps_free(struct Map *);
@ -103,28 +104,28 @@ forceinline optimizespeed int __maps_search(const void *key,
return (addr > map->addr) - (addr < map->addr);
}
dontinstrument static inline struct Map *__maps_next(struct Map *map) {
static inline struct Map *__maps_next(struct Map *map) {
struct Tree *node;
if ((node = tree_next(&map->tree)))
return MAP_TREE_CONTAINER(node);
return 0;
}
dontinstrument static inline struct Map *__maps_prev(struct Map *map) {
static inline struct Map *__maps_prev(struct Map *map) {
struct Tree *node;
if ((node = tree_prev(&map->tree)))
return MAP_TREE_CONTAINER(node);
return 0;
}
dontinstrument static inline struct Map *__maps_first(void) {
static inline struct Map *__maps_first(void) {
struct Tree *node;
if ((node = tree_first(__maps.maps)))
return MAP_TREE_CONTAINER(node);
return 0;
}
dontinstrument static inline struct Map *__maps_last(void) {
static inline struct Map *__maps_last(void) {
struct Tree *node;
if ((node = tree_last(__maps.maps)))
return MAP_TREE_CONTAINER(node);

View file

@ -49,7 +49,7 @@
#include "libc/thread/thread.h"
#include "libc/thread/tls.h"
#define MMDEBUG 1
#define MMDEBUG 0
#define MAX_SIZE 0x0ff800000000ul
#define MAP_FIXED_NOREPLACE_linux 0x100000
@ -94,8 +94,11 @@ privileged optimizespeed struct Map *__maps_floor(const char *addr) {
}
static bool __maps_overlaps(const char *addr, size_t size) {
struct Map *map, *floor = __maps_floor(addr);
for (map = floor; map && map->addr <= addr + size; map = __maps_next(map))
struct Map *map;
ASSERT(__maps_held());
if (!(map = __maps_floor(addr)))
map = __maps_first();
for (; map && map->addr <= addr + size; map = __maps_next(map))
if (MAX(addr, map->addr) <
MIN(addr + PGUP(size), map->addr + PGUP(map->size)))
return true;
@ -105,30 +108,33 @@ static bool __maps_overlaps(const char *addr, size_t size) {
// returns true if all fragments of all allocations which overlap
// [addr,addr+size) are completely contained by [addr,addr+size).
textwindows static bool __maps_envelops(const char *addr, size_t size) {
struct Map *map, *next;
struct Map *map;
size = PGUP(size);
ASSERT(__maps_held());
if (!(map = __maps_floor(addr)))
if (!(map = __maps_first()))
return true;
do {
if (MAX(addr, map->addr) >= MIN(addr + size, map->addr + map->size))
break; // didn't overlap mapping
if (!__maps_isalloc(map))
return false; // didn't include first fragment of alloc
if (addr > map->addr)
return false; // excluded leading pages of first fragment
// set map to last fragment in allocation
for (; (next = __maps_next(map)) && !__maps_isalloc(next); map = next)
// fragments within an allocation must be perfectly contiguous
ASSERT(map->addr + map->size == next->addr);
if (addr + size < map->addr + PGUP(map->size))
return false; // excluded trailing pages of allocation
} while ((map = next));
map = __maps_first();
while (map && map->addr <= addr + size) {
if (MAX(addr, map->addr) < MIN(addr + size, map->addr + map->size)) {
if (!__maps_isalloc(map))
return false; // didn't include first fragment of alloc
if (addr > map->addr)
return false; // excluded leading pages of first fragment
struct Map *next; // set map to last fragment in allocation
for (; (next = __maps_next(map)) && !__maps_isalloc(next); map = next)
ASSERT(map->addr + map->size == next->addr); // contiguous
if (addr + size < map->addr + PGUP(map->size))
return false; // excluded trailing pages of allocation
map = next;
} else {
map = __maps_next(map);
}
}
return true;
}
void __maps_check(void) {
#if MMDEBUG
ASSERT(__maps_held());
size_t maps = 0;
size_t pages = 0;
static unsigned mono;
@ -152,6 +158,22 @@ void __maps_check(void) {
#endif
}
#if MMDEBUG
static void __maps_ok(void) {
ASSERT(!__maps_reentrant());
__maps_lock();
__maps_check();
__maps_unlock();
}
__attribute__((__constructor__)) static void __maps_ctor(void) {
atexit(__maps_ok);
__maps_ok();
}
__attribute__((__destructor__)) static void __maps_dtor(void) {
__maps_ok();
}
#endif
static int __muntrack(char *addr, size_t size, struct Map **deleted,
struct Map **untracked, struct Map temp[2]) {
int rc = 0;
@ -159,13 +181,14 @@ static int __muntrack(char *addr, size_t size, struct Map **deleted,
struct Map *map;
struct Map *next;
size = PGUP(size);
ASSERT(__maps_held());
if (!(map = __maps_floor(addr)))
map = __maps_first();
for (; map && map->addr <= addr + size; map = next) {
next = __maps_next(map);
char *map_addr = map->addr;
size_t map_size = map->size;
if (!(MAX(addr, map_addr) < MIN(addr + size, map_addr + PGUP(map_size))))
if (MAX(addr, map_addr) >= MIN(addr + size, map_addr + PGUP(map_size)))
continue;
if (addr <= map_addr && addr + size >= map_addr + PGUP(map_size)) {
if (map->hand == MAPS_RESERVATION)
@ -350,6 +373,7 @@ static bool __maps_mergeable(const struct Map *x, const struct Map *y) {
void __maps_insert(struct Map *map) {
struct Map *left, *right;
ASSERT(map->size);
ASSERT(__maps_held());
ASSERT(!__maps_overlaps(map->addr, map->size));
__maps.pages += (map->size + __pagesize - 1) / __pagesize;
@ -460,7 +484,7 @@ static int __munmap(char *addr, size_t size) {
return einval();
// test for signal handler tragedy
if (__maps_held())
if (__maps_reentrant())
return edeadlk();
// lock the memory manager
@ -469,8 +493,10 @@ static int __munmap(char *addr, size_t size) {
// on windows we can only unmap whole allocations
if (IsWindows())
if (!__maps_envelops(addr, size))
if (!__maps_envelops(addr, size)) {
__maps_unlock();
return enotsup();
}
// untrack mappings
int rc;
@ -500,6 +526,7 @@ void *__maps_randaddr(void) {
}
static void *__maps_pickaddr(size_t size) {
ASSERT(__maps_held());
char *addr = 0;
struct Map *map, *prev;
size = GRUP(size);
@ -569,11 +596,15 @@ static void *__mmap_impl(char *addr, size_t size, int prot, int flags, int fd,
noreplace = true;
sysflags |= MAP_FIXED_NOREPLACE_linux;
} else if (IsFreebsd() || IsNetbsd()) {
// todo: insert a reservation like windows
sysflags |= MAP_FIXED;
__maps_lock();
if (__maps_overlaps(addr, size)) {
__maps_unlock();
__maps_free(map);
return (void *)eexist();
}
__maps_unlock();
} else {
noreplace = true;
}
@ -729,7 +760,7 @@ static void *__mmap(char *addr, size_t size, int prot, int flags, int fd,
return (void *)enomem();
// test for signal handler reentry
if (__maps_held())
if (__maps_reentrant())
return (void *)edeadlk();
// create memory mappping
@ -874,7 +905,7 @@ static void *__mremap(char *old_addr, size_t old_size, size_t new_size,
return (void *)enomem();
// test for signal handler reentry
if (__maps_held())
if (__maps_reentrant())
return (void *)edeadlk();
// lock the memory manager

View file

@ -67,16 +67,17 @@ int __mprotect(char *addr, size_t size, int prot) {
size = (size + pagesz - 1) & -pagesz;
// test for signal handler reentry
if (__maps_held())
if (__maps_reentrant())
return edeadlk();
// change mappings
int rc = 0;
bool found = false;
__maps_lock();
struct Map *map, *floor;
floor = __maps_floor(addr);
for (map = floor; map && map->addr <= addr + size; map = __maps_next(map)) {
struct Map *map;
if (!(map = __maps_floor(addr)))
map = __maps_first();
for (; map && map->addr <= addr + size; map = __maps_next(map)) {
char *map_addr = map->addr;
size_t map_size = map->size;
char *beg = MAX(addr, map_addr);
@ -85,7 +86,7 @@ int __mprotect(char *addr, size_t size, int prot) {
continue;
found = true;
if (addr <= map_addr && addr + size >= map_addr + PGUP(map_size)) {
// change protection of entire mapping
// change protection status of pages
if (!__mprotect_chunk(map_addr, map_size, prot, map->iscow)) {
map->prot = prot;
} else {

View file

@ -31,32 +31,29 @@ textwindows int sys_msync_nt(char *addr, size_t size, int flags) {
if ((uintptr_t)addr & (__pagesize - 1))
return einval();
if (__maps_held())
if (__maps_reentrant())
return edeadlk();
int rc = 0;
__maps_lock();
struct Map *map, *next;
struct Map *next, *map;
if (!(map = __maps_floor(addr)))
if (!(map = __maps_first()))
return true;
for (; map; map = next) {
map = __maps_first();
for (; map && map->addr <= addr + size; map = next) {
next = __maps_next(map);
if (!__maps_isalloc(map))
continue;
if (map->flags & MAP_ANONYMOUS)
continue;
if (MAX(addr, map->addr) >= MIN(addr + size, map->addr + map->size))
break; // didn't overlap mapping
continue; // didn't overlap mapping
// get true size of win32 allocation
size_t allocsize = map->size;
for (struct Map *map2 = next; map2; map2 = __maps_next(map2)) {
if (!__maps_isalloc(map2) && map->addr + allocsize == map2->addr) {
allocsize += map2->size;
} else {
break;
}
while (next && !__maps_isalloc(next) &&
next->addr + allocsize == next->addr) {
allocsize += next->size;
next = __maps_next(next);
}
// perform the flush

View file

@ -47,6 +47,7 @@
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/runtime/syslib.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"

View file

@ -82,11 +82,7 @@ void ShowCrashReports(void) {
ss.ss_sp = crashstack;
unassert(!sigaltstack(&ss, 0));
InstallCrashHandler(SIGQUIT, 0);
#ifdef __x86_64__
InstallCrashHandler(SIGTRAP, 0);
#else
InstallCrashHandler(SIGTRAP, 0);
#endif
InstallCrashHandler(SIGFPE, 0);
InstallCrashHandler(SIGILL, 0);
InstallCrashHandler(SIGBUS, 0);

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/internal.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/errno.h"
#include "libc/intrin/directmap.h"

View file

@ -24,7 +24,6 @@
#include "libc/errno.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/dll.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.h"
#include "libc/nt/console.h"
#include "libc/nt/enum/creationdisposition.h"
@ -84,6 +83,23 @@ textwindows int sys_kill_nt(int pid, int sig) {
}
}
// attempt to signal via shared memory file
//
// now that we know the process exists, if it has a shared memory file
// then we can be reasonably certain it's a cosmo process which should
// be trusted to deliver its signal, unless it's a nine exterminations
if (pid > 0 && sig != 9) {
atomic_ulong *sigproc;
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
if (sig > 0)
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
memory_order_release);
UnmapViewOfFile(sigproc);
if (sig != 9)
return 0;
}
}
// find existing handle we own for process
//
// this step should come first to verify process existence. this is
@ -91,31 +107,9 @@ textwindows int sys_kill_nt(int pid, int sig) {
// file exists, the process actually exists.
int64_t handle, closeme = 0;
if (!(handle = __proc_handle(pid))) {
if ((handle = OpenProcess(kNtProcessTerminate, false, pid))) {
STRACE("warning: kill() using raw win32 pid");
closeme = handle;
} else {
goto OnError;
}
}
// attempt to signal via shared memory file
//
// now that we know the process exists, if it has a shared memory file
// then we can be reasonably certain it's a cosmo process which should
// be trusted to deliver its signal, unless it's a nine exterminations
if (pid > 0) {
atomic_ulong *sigproc;
if ((sigproc = __sig_map_process(pid, kNtOpenExisting))) {
if (sig > 0)
atomic_fetch_or_explicit(sigproc, 1ull << (sig - 1),
memory_order_release);
UnmapViewOfFile(sigproc);
if (closeme)
CloseHandle(closeme);
if (sig != 9)
return 0;
}
if (!(handle = OpenProcess(kNtProcessTerminate, false, pid)))
return eperm();
closeme = handle;
}
// perform actual kill
@ -127,16 +121,7 @@ textwindows int sys_kill_nt(int pid, int sig) {
CloseHandle(closeme);
if (ok)
return 0;
// handle error
OnError:
switch (GetLastError()) {
case kNtErrorInvalidHandle:
case kNtErrorInvalidParameter:
return esrch();
default:
return eperm();
}
return esrch();
}
#endif /* __x86_64__ */

View file

@ -112,7 +112,7 @@ __msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
__msabi extern typeof(TlsSetValue) *const __imp_TlsSetValue;
__msabi extern typeof(WakeByAddressAll) *const __imp_WakeByAddressAll;
static textwindows dontinstrument wontreturn void //
textwindows dontinstrument wontreturn static void //
WinThreadEntry(int rdi, // rcx
int rsi, // rdx
int rdx, // r8
@ -185,7 +185,7 @@ asm("XnuThreadThunk:\n\t"
".size\tXnuThreadThunk,.-XnuThreadThunk");
__attribute__((__used__))
static wontreturn void
static dontinstrument wontreturn void
XnuThreadMain(void *pthread, // rdi
int tid, // rsi
int (*func)(void *arg, int tid), // rdx
@ -265,7 +265,7 @@ static errno_t CloneXnu(int (*fn)(void *), char *stk, size_t stksz, int flags,
// we can't use address sanitizer because:
// 1. __asan_handle_no_return wipes stack [todo?]
relegated static wontreturn void OpenbsdThreadMain(void *p) {
relegated dontinstrument wontreturn static void OpenbsdThreadMain(void *p) {
struct CloneArgs *wt = p;
atomic_init(wt->ptid, wt->tid);
atomic_init(wt->ctid, wt->tid);
@ -318,11 +318,12 @@ relegated errno_t CloneOpenbsd(int (*func)(void *, int), char *stk,
////////////////////////////////////////////////////////////////////////////////
// NET BESIYATA DISHMAYA
static wontreturn void NetbsdThreadMain(void *arg, // rdi
int (*func)(void *, int), // rsi
int flags, // rdx
atomic_int *ctid, // rcx
atomic_int *ptid) { // r8
wontreturn dontinstrument static void NetbsdThreadMain(
void *arg, // rdi
int (*func)(void *, int), // rsi
int flags, // rdx
atomic_int *ctid, // rcx
atomic_int *ptid) { // r8
int ax, dx;
static atomic_int clobber;
atomic_int *ztid = &clobber;
@ -420,7 +421,7 @@ static int CloneNetbsd(int (*func)(void *, int), char *stk, size_t stksz,
////////////////////////////////////////////////////////////////////////////////
// FREE BESIYATA DISHMAYA
static wontreturn void FreebsdThreadMain(void *p) {
wontreturn dontinstrument static void FreebsdThreadMain(void *p) {
struct CloneArgs *wt = p;
#ifdef __aarch64__
asm volatile("mov\tx28,%0" : /* no outputs */ : "r"(wt->tls));
@ -519,7 +520,7 @@ static errno_t CloneFreebsd(int (*func)(void *, int), char *stk, size_t stksz,
////////////////////////////////////////////////////////////////////////////////
// APPLE SILICON
static void *SiliconThreadMain(void *arg) {
dontinstrument static void *SiliconThreadMain(void *arg) {
struct CloneArgs *wt = arg;
asm volatile("mov\tx28,%0" : /* no outputs */ : "r"(wt->tls));
atomic_init(wt->ctid, wt->this);
@ -595,7 +596,7 @@ int sys_clone_linux(int flags, // rdi
void *func, // r9
void *arg); // 8(rsp)
static int LinuxThreadEntry(void *arg, int tid) {
dontinstrument static int LinuxThreadEntry(void *arg, int tid) {
struct LinuxCloneArgs *wt = arg;
#if defined(__x86_64__)
sys_set_tls(ARCH_SET_GS, wt->tls);

View file

@ -119,6 +119,7 @@ privileged int __hook(void *dest, struct SymbolTable *st) {
if (!st)
return -1;
__morph_begin();
__jit_begin();
lowest = MAX((intptr_t)__executable_start, (intptr_t)_ereal);
for (i = 0; i < st->count; ++i) {
if (st->symbols[i].x < 9)
@ -138,6 +139,9 @@ privileged int __hook(void *dest, struct SymbolTable *st) {
// kprintf("can't hook %t at %lx\n", p, p);
}
}
__clear_cache(MAX((char *)__executable_start, (char *)_ereal),
MIN((char *)__privileged_start, (char *)_etext));
__jit_end();
__morph_end();
return 0;
}

View file

@ -20,7 +20,7 @@
#include "libc/runtime/runtime.h"
#include "libc/runtime/syslib.internal.h"
void __jit_begin(void) {
privileged void __jit_begin(void) {
if (IsXnuSilicon()) {
if (__syslib->__pthread_jit_write_protect_supported_np()) {
__syslib->__pthread_jit_write_protect_np(false);
@ -28,7 +28,7 @@ void __jit_begin(void) {
}
}
void __jit_end(void) {
privileged void __jit_end(void) {
if (IsXnuSilicon()) {
if (__syslib->__pthread_jit_write_protect_supported_np()) {
__syslib->__pthread_jit_write_protect_np(true);

View file

@ -16,7 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/kprintf.h"
#include "libc/macros.h"
#include "libc/runtime/zipos.internal.h"
#include "libc/str/str.h"
@ -44,9 +43,8 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
// strip trailing slash from search name
int len = name->len;
if (len && name->path[len - 1] == '/') {
if (len && name->path[len - 1] == '/')
--len;
}
// empty string means the /zip root directory
if (!len) {
@ -91,9 +89,8 @@ ssize_t __zipos_scan(struct Zipos *zipos, struct ZiposUri *name) {
dx = dx < -1 ? -1 : dx;
for (l += dx; 0 <= l && l < zipos->records; l += dx) {
ssize_t cf;
if ((cf = __zipos_match(zipos, name, len, l)) != -1) {
if ((cf = __zipos_match(zipos, name, len, l)) != -1)
return cf;
}
cfile = zipos->index[l];
zname = ZIP_CFILE_NAME(zipos->map + cfile);
zsize = ZIP_CFILE_NAMESIZE(zipos->map + cfile);

View file

@ -118,9 +118,10 @@ int main(int argc, char *argv[]) {
GetOpts(argc, argv);
for (fd = 3; fd < 100; ++fd) {
int oe = errno;
for (fd = 3; fd < 100; ++fd)
close(fd);
}
errno = oe;
#ifndef TINY
setenv("GDB", "", true);

View file

@ -94,7 +94,6 @@ TEST(dup2, zipossrc) {
ASSERT_SYS(0, 0, close(3));
}
#ifdef __x86_64__
TEST(dup, clearsCloexecFlag) {
static bool once;
int ws;
@ -112,4 +111,3 @@ TEST(dup, clearsCloexecFlag) {
ASSERT_EQ(72 << 8, ws);
ASSERT_SYS(0, 0, close(3));
}
#endif

View file

@ -39,8 +39,6 @@
#include "libc/x/xsigaction.h"
#include "libc/x/xspawn.h"
#ifdef __x86_64__
#define MEM (64 * 1024 * 1024)
static char tmpname[PATH_MAX];
@ -104,7 +102,7 @@ TEST(setrlimit, testFileSizeLimit) {
firstnonnull(getenv("TMPDIR"), "/tmp"),
firstnonnull(program_invocation_short_name, "unknown"), getpid());
ASSERT_NE(-1, (fd = open(tmpname, O_RDWR | O_CREAT | O_TRUNC, 0644)));
rngset(junkdata, 512, _rand64, -1);
rngset(junkdata, 512, lemur64, -1);
for (i = 0; i < 5 * 1024 * 1024 / 512; ++i) {
ASSERT_EQ(512, write(fd, junkdata, 512));
}
@ -143,7 +141,7 @@ TEST(setrlimit, testMemoryLimit) {
ASSERT_EQ(ENOMEM, errno);
_Exit(0);
}
rngset(p, getpagesize(), _rand64, -1);
rngset(p, getpagesize(), lemur64, -1);
}
_Exit(1);
}
@ -160,14 +158,13 @@ TEST(setrlimit, testVirtualMemoryLimit) {
if (wstatus == -2) {
ASSERT_EQ(0, setrlimit(RLIMIT_AS, &(struct rlimit){MEM, MEM}));
for (i = 0; i < (MEM * 2) / getpagesize(); ++i) {
p = sys_mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)
.addr;
if (p == MAP_FAILED) {
if ((p = mmap(0, getpagesize(), PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0)) ==
MAP_FAILED) {
ASSERT_EQ(ENOMEM, errno);
_Exit(0);
}
rngset(p, getpagesize(), _rand64, -1);
rngset(p, getpagesize(), lemur64, -1);
}
_Exit(1);
}
@ -201,7 +198,7 @@ TEST(setrlimit, testDataMemoryLimit) {
ASSERT_EQ(ENOMEM, errno);
_Exit(0);
}
rngset(p, getpagesize(), _rand64, -1);
rngset(p, getpagesize(), lemur64, -1);
}
_Exit(1);
}
@ -243,5 +240,3 @@ TEST(setrlimit, isVforkSafe) {
EXPECT_EQ(rlim[0].rlim_cur, rlim[1].rlim_cur);
EXPECT_EQ(rlim[0].rlim_max, rlim[1].rlim_max);
}
#endif /* __x86_64__ */

View file

@ -1,105 +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/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
void OnUsr1(int sig) {
_exit(0);
}
void SetUpOnce(void) {
sigset_t ss;
sigprocmask(SIG_SETMASK, 0, &ss);
ASSERT_SYS(0, 0, pledge("stdio proc", 0));
}
TEST(signal, test) {
ASSERT_NE(SIG_ERR, signal(SIGUSR1, OnUsr1));
ASSERT_NE(-1, raise(SIGUSR1));
__die();
}
////////////////////////////////////////////////////////////////////////////////
// signal round-trip delivery takes about 1µs
void OnSigTrap(int sig, siginfo_t *si, void *ctx) {
}
void TrapBench(int n) {
for (int i = 0; i < n; ++i) {
DebugBreak();
}
}
BENCH(signal, trapBench) {
struct sigaction old;
struct sigaction sabus = {.sa_sigaction = OnSigTrap};
ASSERT_SYS(0, 0, sigaction(SIGTRAP, &sabus, &old));
EZBENCH_N("signal trap", 16, TrapBench(16));
EZBENCH_N("signal trap", 256, TrapBench(256));
EZBENCH_N("signal trap", 1024, TrapBench(1024));
sigaction(SIGTRAP, &old, 0);
}
BENCH(signal, trapBenchSiginfo) {
struct sigaction old;
struct sigaction sabus = {.sa_sigaction = OnSigTrap, .sa_flags = SA_SIGINFO};
ASSERT_SYS(0, 0, sigaction(SIGTRAP, &sabus, &old));
EZBENCH_N("siginfo trap", 16, TrapBench(16));
EZBENCH_N("siginfo trap", 256, TrapBench(256));
EZBENCH_N("siginfo trap", 1024, TrapBench(1024));
sigaction(SIGTRAP, &old, 0);
}
#ifdef __x86_64__
void OnSigHlt(int sig, siginfo_t *si, void *vctx) {
struct ucontext *ctx = vctx;
ctx->uc_mcontext.rip += 1;
}
void HltBench(int n) {
for (int i = 0; i < n; ++i) {
asm("hlt");
}
}
BENCH(signal, hltBenchSiginfo) {
struct sigaction old[2];
struct sigaction sabus = {.sa_sigaction = OnSigHlt, .sa_flags = SA_SIGINFO};
ASSERT_SYS(0, 0, sigaction(SIGSEGV, &sabus, old + 0));
ASSERT_SYS(0, 0, sigaction(SIGBUS, &sabus, old + 1));
EZBENCH_N("siginfo hlt", 16, HltBench(16));
EZBENCH_N("siginfo hlt", 256, HltBench(256));
EZBENCH_N("siginfo hlt", 1024, HltBench(1024));
sigaction(SIGSEGV, old + 0, 0);
sigaction(SIGBUS, old + 1, 0);
}
#endif /* __x86_64__ */

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -27,12 +28,14 @@
#include "libc/runtime/sysconf.h"
#include "libc/stdio/rand.h"
#include "libc/stdio/stdio.h"
#include "libc/stdio/sysparam.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/testlib/benchmark.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
#include "libc/x/xspawn.h"
@ -56,6 +59,10 @@ void SetUpOnce(void) {
// ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc", 0));
}
void TearDown(void) {
ASSERT_FALSE(__maps_held());
}
TEST(mmap, zeroSize) {
ASSERT_SYS(EINVAL, MAP_FAILED,
mmap(NULL, 0, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
@ -331,6 +338,172 @@ TEST(mmap, pml5t) {
}
}
TEST(mmap, windows) {
if (!IsWindows())
return;
int count = __maps.count;
char *base = __maps_randaddr();
ASSERT_EQ(base, mmap(base, pagesz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_EQ((count += 1), __maps.count);
// isn't granularity aligned
ASSERT_SYS(EINVAL, -1, munmap(base + pagesz, pagesz));
// doesn't overlap any maps
ASSERT_SYS(0, 0, munmap(base + gransz, pagesz));
ASSERT_EQ(count, __maps.count);
// doesn't overlap any maps
ASSERT_SYS(0, 0, munmap(base - gransz, gransz));
ASSERT_EQ(count, __maps.count);
// partially overlaps map
ASSERT_SYS(ENOTSUP, -1, munmap(base, pagesz));
ASSERT_EQ(count, __maps.count);
// envelops map
ASSERT_SYS(0, 0, munmap(base - gransz, gransz + pagesz * 4));
ASSERT_EQ((count -= 1), __maps.count);
// win32 actually unmapped map
ASSERT_EQ(base, mmap(base, pagesz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_EQ((count += 1), __maps.count);
// change status of middle page results in three fragments
ASSERT_SYS(0, 0, mprotect(base + pagesz, pagesz, PROT_NONE));
ASSERT_EQ((count += 2), __maps.count);
// change status back (todo: should reunite fragments)
ASSERT_SYS(0, 0, mprotect(base + pagesz, pagesz, PROT_READ | PROT_WRITE));
ASSERT_EQ(count, __maps.count);
// clean up
ASSERT_SYS(0, 0, munmap(base, pagesz * 3));
ASSERT_EQ((count -= 3), __maps.count);
}
TEST(mmap, windows_partial_overlap_enotsup) {
if (!IsWindows())
return;
int count = __maps.count;
char *base = __maps_randaddr();
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_EQ((count += 1), __maps.count);
// partially overlaps on left
ASSERT_SYS(ENOTSUP, -1, munmap(base - gransz, gransz * 2));
ASSERT_SYS(ENOTSUP, -1, munmap(base, gransz * 2));
ASSERT_EQ(count, __maps.count);
// partially overlaps the middle
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz));
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz * 2));
ASSERT_EQ(count, __maps.count);
// partially overlaps on right
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 2, gransz * 2));
ASSERT_EQ(count, __maps.count);
// doesn't overlap any maps
ASSERT_SYS(0, 0, munmap(base - gransz, gransz));
ASSERT_SYS(0, 0, munmap(base + gransz * 3, gransz));
ASSERT_EQ(count, __maps.count);
// unmap envelops
ASSERT_SYS(0, 0, munmap(base - gransz, gransz * 4));
ASSERT_EQ((count -= 1), __maps.count);
// win32 actually removed the memory
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_EQ((count += 1), __maps.count);
// clean up
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
ASSERT_EQ((count -= 1), __maps.count);
}
TEST(munmap, windows_not_all_fragments_included_enotsup) {
if (!IsWindows())
return;
int count = __maps.count;
char *base = __maps_randaddr();
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_EQ((count += 1), __maps.count);
// win32 memory actually exists
ASSERT_SYS(EEXIST, MAP_FAILED,
mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_SYS(EEXIST, MAP_FAILED,
mmap(base + gransz * 0, gransz, PROT_READ | PROT_WRITE,
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_SYS(EEXIST, MAP_FAILED,
mmap(base + gransz * 1, gransz, PROT_READ | PROT_WRITE,
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_SYS(EEXIST, MAP_FAILED,
mmap(base + gransz * 2, gransz, PROT_READ | PROT_WRITE,
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
// change status of middle page results in three fragments
ASSERT_SYS(0, 0, mprotect(base + gransz, gransz, PROT_NONE));
ASSERT_EQ((count += 2), __maps.count);
// partially overlaps on left
ASSERT_SYS(ENOTSUP, -1, munmap(base - gransz, gransz * 2));
ASSERT_SYS(ENOTSUP, -1, munmap(base, gransz * 2));
ASSERT_EQ(count, __maps.count);
// partially overlaps the middle
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz));
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 1, gransz * 2));
ASSERT_EQ(count, __maps.count);
// partially overlaps on right
ASSERT_SYS(ENOTSUP, -1, munmap(base + gransz * 2, gransz * 2));
ASSERT_EQ(count, __maps.count);
// doesn't overlap any maps
ASSERT_SYS(0, 0, munmap(base - gransz, gransz));
ASSERT_SYS(0, 0, munmap(base + gransz * 3, gransz));
ASSERT_EQ(count, __maps.count);
// unmap envelops
ASSERT_SYS(0, 0, munmap(base - gransz, gransz * 4));
ASSERT_EQ((count -= 3), __maps.count);
// win32 actually removed the memory
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_EQ((count += 1), __maps.count);
// clean up
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
ASSERT_EQ((count -= 1), __maps.count);
}
TEST(mmap, windows_private_memory_fork_uses_virtualfree) {
if (IsFreebsd())
return; // freebsd can't take a hint
char *base;
ASSERT_NE(MAP_FAILED, (base = mmap(0, gransz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)));
SPAWN(fork);
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
ASSERT_EQ(base, mmap(base, gransz * 3, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
EXITS(0);
ASSERT_SYS(0, 0, munmap(base, gransz * 3));
}
////////////////////////////////////////////////////////////////////////////////
// zipos NON-SHARED READ-ONLY FILE MEMORY

View file

@ -178,6 +178,9 @@ void search_test(void) {
// ↑ ↑ ↑
// 4 3 8
//
x = tree_floor(tree, (void *)0l, number_search);
if (x)
exit(4);
x = tree_floor(tree, (void *)4l, number_search);
if (!x)
exit(4);

View file

@ -5,14 +5,8 @@ PKGS += TEST_LIBC_LOG
TEST_LIBC_LOG_SRCS := $(wildcard test/libc/log/*.c)
TEST_LIBC_LOG_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_LOG_SRCS))
TEST_LIBC_LOG_OBJS = \
$(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.o) \
o/$(MODE)/test/libc/log/backtrace.zip.o \
o/$(MODE)/test/libc/log/backtrace.dbg.zip.o
TEST_LIBC_LOG_COMS = \
$(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%)
TEST_LIBC_LOG_OBJS = $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%.o)
TEST_LIBC_LOG_COMS = $(TEST_LIBC_LOG_SRCS:%.c=o/$(MODE)/%)
TEST_LIBC_LOG_BINS = \
$(TEST_LIBC_LOG_COMS) \
@ -26,19 +20,17 @@ TEST_LIBC_LOG_CHECKS = \
TEST_LIBC_LOG_DIRECTDEPS = \
LIBC_CALLS \
LIBC_RUNTIME \
NET_HTTP \
LIBC_STDIO \
LIBC_X \
LIBC_INTRIN \
LIBC_FMT \
LIBC_INTRIN \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_LOG \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STR \
LIBC_TESTLIB \
LIBC_SYSV \
LIBC_LOG
LIBC_TESTLIB \
TEST_LIBC_LOG_DEPS := \
$(call uniq,$(foreach x,$(TEST_LIBC_LOG_DIRECTDEPS),$($(x))))
@ -56,29 +48,6 @@ o/$(MODE)/test/libc/log/%.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/log/backtrace_test.dbg: \
$(TEST_LIBC_LOG_DEPS) \
o/$(MODE)/test/libc/log/backtrace.zip.o \
o/$(MODE)/test/libc/log/backtrace.dbg.zip.o \
o/$(MODE)/test/libc/log/backtrace_test.o \
o/$(MODE)/test/libc/log/log.pkg \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/log/backtrace.dbg: \
$(TEST_LIBC_LOG_DEPS) \
o/$(MODE)/test/libc/log/backtrace.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/log/backtrace.zip.o \
o/$(MODE)/test/libc/log/backtrace.dbg.zip.o: private \
ZIPOBJ_FLAGS += \
-B
.PHONY: o/$(MODE)/test/libc/log
o/$(MODE)/test/libc/log: \
$(TEST_LIBC_LOG_BINS) \

View file

@ -1,154 +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 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/fmt/conv.h"
#include "libc/intrin/weaken.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/leaks.h"
#include "libc/mem/mem.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#ifdef __x86_64__
#include <stdlib.h>
int StackOverflow(int d) {
char A[8];
for (int i = 0; i < sizeof(A); i++)
A[i] = d + i;
if (__veil("r", d))
return StackOverflow(d + 1) + A[d % sizeof(A)];
return 0;
}
void FpuCrash(void) {
typedef char xmm_t __attribute__((__vector_size__(16)));
xmm_t v = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf};
volatile int x = 0;
asm volatile("fldpi");
asm volatile("mov\t%0,%%r15" : /* no outputs */ : "g"(0x3133731337));
asm volatile("movaps\t%0,%%xmm15" : /* no outputs */ : "x"(v));
fputc(7 / x, stdout);
}
char bss[10];
void BssOverrunCrash(int n) {
int i;
for (i = 0; i < n; ++i) {
bss[i] = i;
}
}
char data[10] = "abcdeabcde";
void DataOverrunCrash(int n) {
int i;
for (i = 0; i < n; ++i) {
data[i] = i;
}
}
const char rodata[10] = "abcdeabcde";
int RodataOverrunCrash(int i) {
return rodata[i];
}
char *StackOverrunCrash(int n) {
int i;
char stack[10];
bzero(stack, sizeof(stack));
for (i = 0; i < n; ++i) {
stack[i] = i;
}
return strdup(stack);
}
char *MemoryLeakCrash(void) {
char *p = strdup("doge");
CheckForMemoryLeaks();
return p;
}
int NpeCrash(char *p) {
asm("nop"); // xxx: due to backtrace addr-1 thing
return *p;
}
int StackOverflowCrash(int d) {
char A[8];
for (int i = 0; i < sizeof(A); i++)
A[i] = d + i;
if (__veil("r", d))
return StackOverflowCrash(d + 1) + A[d % sizeof(A)];
return 0;
}
void (*pFpuCrash)(void) = FpuCrash;
void (*pBssOverrunCrash)(int) = BssOverrunCrash;
void (*pDataOverrunCrash)(int) = DataOverrunCrash;
int (*pRodataOverrunCrash)(int) = RodataOverrunCrash;
char *(*pMemoryLeakCrash)(void) = MemoryLeakCrash;
int (*pNpeCrash)(char *) = NpeCrash;
int main(int argc, char *argv[]) {
ShowCrashReports();
if (argc > 1) {
switch (atoi(argv[1])) {
case 0:
break;
case 1:
pFpuCrash();
exit(0);
case 2:
pBssOverrunCrash(10 + 1);
exit(0);
case 3:
exit(pRodataOverrunCrash(10 + 1));
case 4:
pDataOverrunCrash(10 + 1);
exit(0);
case 5:
exit(StackOverflowCrash(0));
case 6:
exit((intptr_t)pMemoryLeakCrash());
case 7:
exit(pNpeCrash(0));
case 8:
exit(pNpeCrash(0));
case 9:
exit(StackOverflow(0));
default:
fputs("error: unrecognized argument\n", stderr);
exit(1);
}
} else {
fputs("error: too few args\n", stderr);
exit(1);
}
}
#else
int main(int argc, char *argv[]) {
}
#endif /* __x86_64__ */

View file

@ -1,402 +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 2021 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/assert.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/log/libfatal.internal.h"
#include "libc/log/log.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
#include "libc/x/xasprintf.h"
#include "net/http/escape.h"
#ifdef __x86_64__
#if 0
__static_yoink("backtrace");
__static_yoink("backtrace.dbg");
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown_once();
ASSERT_NE(-1, mkdir("bin", 0755));
testlib_extract("/zip/backtrace", "bin/backtrace", 0755);
testlib_extract("/zip/backtrace.dbg", "bin/backtrace.dbg", 0755);
}
static bool OutputHasSymbol(const char *output, const char *s) {
return strstr(output, s) || (!FindDebugBinary() && strstr(output, "NULL"));
}
// UNFREED MEMORY
// o/dbg/test/libc/log/backtrace_test
// max allocated space 655,360
// total allocated space 80
// total free space 327,600
// releasable space 0
// mmaped space 65,360
// non-mmapped space 327,680
//
// 100080040020 64 bytes 5 used
// 421871 strdup
// 416529 MemoryLeakCrash
// 41666d SetUp
// 45428c testlib_runtestcases
//
// 00007fff0000-000080010000 rw-pa-F 2x shadow of 000000000000
// 000080070000-0000800a0000 rw-pa-F 3x shadow of 0000003c0000
// 02008fff0000-020090020000 rw-pa-F 3x shadow of 10007ffc0000
// 020090060000-020090080000 rw-pa-F 2x shadow of 100080340000
// 0e007fff0000-0e0080010000 rw-pa-F 2x shadow of 6ffffffc0000
// 100006560000-100006580000 rw-pa-F 2x shadow of 7ffc32b40000
// 100080000000-100080050000 rw-pa-- 5x automap w/ 50 frame hole
// 100080370000-100080390000 rw-pa-- 2x automap w/ 1 frame hole
// 1000803a0000-1000803b0000 rw-pa-- 1x automap
// 6ffffffe0000-700000000000 rw-paSF 2x stack
// # 24 frames mapped w/ 51 frames gapped
TEST(ShowCrashReports, testMemoryLeakCrash) {
size_t got;
ssize_t rc;
int ws, pid, fds[2];
char *output, buf[512];
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
dup2(fds[1], 1);
dup2(fds[1], 2);
execv("bin/backtrace", (char *const[]){"bin/backtrace", "6", 0});
_Exit(127);
}
close(fds[1]);
output = 0;
appends(&output, "");
for (;;) {
rc = read(fds[0], buf, sizeof(buf));
if (rc == -1) {
ASSERT_EQ(EINTR, errno);
continue;
}
if ((got = rc)) {
appendd(&output, buf, got);
} else {
break;
}
}
close(fds[0]);
ASSERT_NE(-1, wait(&ws));
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
EXPECT_EQ(78 << 8, ws);
ASSERT_TRUE(!!strstr(output, "UNFREED MEMORY"));
if (IsAsan()) {
ASSERT_TRUE(OutputHasSymbol(output, "strdup") &&
OutputHasSymbol(output, "MemoryLeakCrash"));
}
free(output);
}
// error: Uncaught SIGFPE (FPE_INTDIV) on nightmare pid 11724
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721
// ENOTTY[25]
// Linux nightmare SMP Thu, 12 Aug 2021 06:16:45 UTC
//
// 0x0000000000414659: FpuCrash at test/libc/log/backtrace_test.c:35
// 0x000000000045003b: testlib_runtestcases at libc/testlib/testrunner.c:98
// 0x000000000044b770: testlib_runalltests at libc/testlib/runner.c:37
// 0x000000000040278e: main at libc/testlib/testmain.c:86
// 0x0000000000403210: cosmo at libc/runtime/cosmo.S:65
// 0x0000000000402247: _start at libc/crt/crt.S:67
//
// RAX 0000000000000007 RBX 00006fffffffff10 RDI 00007ffe0745fde1 ST(0) 0.0
// RCX 0000000000000000 RDX 0000000000000000 RSI 0000000000489900 ST(1) 0.0
// RBP 00006fffffffff70 RSP 00006fffffffff10 RIP 000000000041465a ST(2) 0.0
// R8 0000000000000001 R9 00006ffffffffcc0 R10 00006ffffffffe60 ST(3) 0.0
// R11 000000000000000d R12 00000dffffffffe2 R13 00006fffffffff10 ST(4) 0.0
// R14 0000000000000003 R15 000000000049b700 VF PF ZF IF
//
// XMM0 00000000000000000000000000000000 XMM8 00000000000000000000000000000000
// XMM1 000000008000000400000000004160ea XMM9 00000000000000000000000000000000
// XMM2 00000000000000000000000000000000 XMM10 00000000000000000000000000000000
// XMM3 00000000000000000000000000000000 XMM11 00000000000000000000000000000000
// XMM4 00000000000000000000000000000000 XMM12 00000000000000000000000000000000
// XMM5 00000000000000000000000000000000 XMM13 00000000000000000000000000000000
// XMM6 00000000000000000000000000000000 XMM14 00000000000000000000000000000000
// XMM7 00000000000000000000000000000000 XMM15 00000000000000000000000000000000
//
// mm->i == 4;
// mm->p[ 0]=={0x00008007,0x00008008,-1,3,50}; /* 2 */
// /* 234,881,012 */
// mm->p[ 1]=={0x0e007ffd,0x0e007fff,-1,3,50}; /* 3 */
// /* 33,538,280 */
// mm->p[ 2]=={0x100040e8,0x100040e8,-1,3,50}; /* 1 */
// /* 1,610,596,103 */
// mm->p[ 3]=={0x6ffffff0,0x6fffffff,12884901888,306,0}; /* 16 */
// /* 22 frames mapped w/ 1,879,015,395 frames gapped */
//
// 00400000-0045b000 r-xp 00000000 08:03 4587526
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721
// 0045b000-00461000 rw-p 0005b000 08:03 4587526
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721
// 00461000-004a0000 rw-p 00000000 00:00 0
// 80070000-80090000 rw-p 00000000 00:00 0
// e007ffd0000-e0080000000 rw-p 00000000 00:00 0
// 100040e80000-100040e90000 rw-p 00000000 00:00 0
// 6ffffff00000-700000000000 rw-p 00000000 00:00 0
// 7ffe0743f000-7ffe07460000 rw-p 00000000 00:00 0 [stack]
// 7ffe075a8000-7ffe075ab000 r--p 00000000 00:00 0 [vvar]
// 7ffe075ab000-7ffe075ac000 r-xp 00000000 00:00 0 [vdso]
//
// /home/jart/cosmo/o/dbg/test/libc/log/backtrace_test.tmp.11721 1
TEST(ShowCrashReports, testDivideByZero) {
size_t got;
ssize_t rc;
int ws, pid, fds[2];
char *output, buf[512];
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
dup2(fds[1], 1);
dup2(fds[1], 2);
execv("bin/backtrace", (char *const[]){"bin/backtrace", "1", 0});
_Exit(127);
}
close(fds[1]);
output = 0;
appends(&output, "");
for (;;) {
rc = read(fds[0], buf, sizeof(buf));
if (rc == -1) {
ASSERT_EQ(EINTR, errno);
continue;
}
if ((got = rc)) {
appendd(&output, buf, got);
} else {
break;
}
}
close(fds[0]);
ASSERT_NE(-1, wait(&ws));
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
if (IsModeDbg()) {
EXPECT_EQ(77 << 8, ws);
} else {
EXPECT_TRUE(WIFSIGNALED(ws));
EXPECT_EQ(SIGFPE, WTERMSIG(ws));
}
/* NULL is stopgap until we can copy symbol tables into binary */
#ifdef __FNO_OMIT_FRAME_POINTER__
ASSERT_TRUE(OutputHasSymbol(output, "FpuCrash"));
#endif
if (strstr(output, "divrem overflow")) {
// UBSAN handled it
} else {
// ShowCrashReports() handled it
if (!strstr(output, gc(xasprintf("%d", pid)))) {
fprintf(stderr, "ERROR: crash report didn't have pid\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
if (!strstr(output, "SIGFPE")) {
fprintf(stderr, "ERROR: crash report didn't have signal name\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
// XXX: WSL doesn't save and restore x87 registers to ucontext_t
if (!__iswsl1()) {
if (!strstr(output, "3.141")) {
fprintf(stderr, "ERROR: crash report didn't have fpu register\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
}
if (!strstr(output, "0f0e0d0c0b0a09080706050403020100")) {
fprintf(stderr, "ERROR: crash report didn't have sse register\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
if (!strstr(output, "3133731337")) {
fprintf(stderr, "ERROR: crash report didn't have general register\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
}
free(output);
}
TEST(ShowCrashReports, testBssOverrunCrash) {
if (!IsAsan()) return;
size_t got;
ssize_t rc;
int ws, pid, fds[2];
char *output, buf[512];
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
dup2(fds[1], 1);
dup2(fds[1], 2);
execv("bin/backtrace", (char *const[]){"bin/backtrace", "2", 0});
_Exit(127);
}
close(fds[1]);
output = 0;
appends(&output, "");
for (;;) {
rc = read(fds[0], buf, sizeof(buf));
if (rc == -1) {
ASSERT_EQ(EINTR, errno);
continue;
}
if ((got = rc)) {
appendd(&output, buf, got);
} else {
break;
}
}
close(fds[0]);
ASSERT_NE(-1, wait(&ws));
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
EXPECT_EQ(77 << 8, ws);
/* NULL is stopgap until we can copy symbol tablces into binary */
#ifdef __FNO_OMIT_FRAME_POINTER__
ASSERT_TRUE(OutputHasSymbol(output, "BssOverrunCrash"));
#endif
if (IsAsan()) {
ASSERT_TRUE(
!!strstr(output, "'int' index 10 into 'char [10]' out of bounds"));
} else {
ASSERT_TRUE(!!strstr(output, "☺☻♥♦♣♠•◘○"));
ASSERT_TRUE(!!strstr(output, "global redzone"));
}
free(output);
}
TEST(ShowCrashReports, testDataOverrunCrash) {
if (!IsAsan()) return;
size_t got;
ssize_t rc;
int ws, pid, fds[2];
char *output, buf[512];
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
dup2(fds[1], 1);
dup2(fds[1], 2);
execv("bin/backtrace", (char *const[]){"bin/backtrace", "4", 0});
_Exit(127);
}
close(fds[1]);
output = 0;
appends(&output, "");
for (;;) {
rc = read(fds[0], buf, sizeof(buf));
if (rc == -1) {
ASSERT_EQ(EINTR, errno);
continue;
}
if ((got = rc)) {
appendd(&output, buf, got);
} else {
break;
}
}
close(fds[0]);
ASSERT_NE(-1, wait(&ws));
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
EXPECT_EQ(77 << 8, ws);
/* NULL is stopgap until we can copy symbol tablces into binary */
#ifdef __FNO_OMIT_FRAME_POINTER__
ASSERT_TRUE(OutputHasSymbol(output, "DataOverrunCrash"));
#endif
if (!strstr(output, "'int' index 10 into 'char [10]' out")) { // ubsan
ASSERT_TRUE(!!strstr(output, "☺☻♥♦♣♠•◘○")); // asan
ASSERT_TRUE(!!strstr(output, "global redzone")); // asan
}
free(output);
}
TEST(ShowCrashReports, testNpeCrashAfterFinalize) {
/*
* this test makes sure we're not doing things like depending on
* environment variables after __cxa_finalize is called in cases
* where putenv() is used
*/
size_t got;
ssize_t rc;
int ws, pid, fds[2];
char *output, buf[512];
ASSERT_NE(-1, pipe2(fds, O_CLOEXEC));
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
dup2(fds[1], 1);
dup2(fds[1], 2);
execv("bin/backtrace", (char *const[]){"bin/backtrace", "8", 0});
_Exit(127);
}
close(fds[1]);
output = 0;
appends(&output, "");
for (;;) {
rc = read(fds[0], buf, sizeof(buf));
if (rc == -1) {
ASSERT_EQ(EINTR, errno);
continue;
}
if ((got = rc)) {
appendd(&output, buf, got);
} else {
break;
}
}
close(fds[0]);
ASSERT_NE(-1, wait(&ws));
// tinyprint(2, gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
if (IsModeDbg()) {
EXPECT_EQ(77 << 8, ws);
} else {
EXPECT_TRUE(WIFSIGNALED(ws));
EXPECT_EQ(SIGSEGV, WTERMSIG(ws));
}
/* NULL is stopgap until we can copy symbol tables into binary */
if (!strstr(output, IsAsan() ? "null pointer" : "Uncaught SIGSEGV (SEGV_")) {
fprintf(stderr, "ERROR: crash report didn't diagnose the problem\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
#ifdef __FNO_OMIT_FRAME_POINTER__
if (!OutputHasSymbol(output, "NpeCrash")) {
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
gc(IndentLines(output, -1, 0, 4)));
__die();
}
#endif
free(output);
}
#endif
#endif /* __x86_64__ */

View file

@ -60,6 +60,17 @@ o/$(MODE)/test/libc/proc/%.dbg: \
o/$(MODE)/test/libc/proc/posix_spawn_test.runs: \
private QUOTA += -M8192m
o/$(MODE)/test/libc/proc/fork_test.dbg: \
$(TEST_LIBC_PROC_DEPS) \
o/$(MODE)/test/libc/proc/fork_test.o \
o/$(MODE)/test/libc/proc/proc.pkg \
o/$(MODE)/tool/hello/life-pe.ape.zip.o \
o/$(MODE)/test/libc/proc/life.zip.o \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/proc/posix_spawn_test.dbg: \
$(TEST_LIBC_PROC_DEPS) \
o/$(MODE)/test/libc/proc/posix_spawn_test.o \
@ -99,6 +110,14 @@ o/$(MODE)/test/libc/proc/fexecve_test.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/proc/life.dbg: \
$(TEST_LIBC_PROC_DEPS) \
o/$(MODE)/test/libc/proc/life.o \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/proc/life.zip.o \
o/$(MODE)/test/libc/proc/execve_test_prog1.zip.o \
o/$(MODE)/test/libc/proc/life-pe.zip.o: private \
ZIPOBJ_FLAGS += \

View file

@ -27,6 +27,7 @@
#include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/proc/posix_spawn.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
@ -39,6 +40,10 @@
#include "libc/testlib/testlib.h"
#include "libc/thread/tls.h"
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown();
}
TEST(fork, testPipes) {
int a, b;
int ws, pid;
@ -142,7 +147,7 @@ TEST(fork, preservesTlsMemory) {
EXITS(0);
}
void ForkInSerial(void) {
void fork_wait_in_serial(void) {
int pid, ws;
ASSERT_NE(-1, (pid = fork()));
if (!pid)
@ -152,7 +157,19 @@ void ForkInSerial(void) {
ASSERT_EQ(0, WEXITSTATUS(ws));
}
void VforkInSerial(void) {
void vfork_execl_wait_in_serial(void) {
int pid, ws;
ASSERT_NE(-1, (pid = vfork()));
if (!pid) {
execl("./life", "./life", NULL);
_Exit(127);
}
ASSERT_NE(-1, waitpid(pid, &ws, 0));
ASSERT_TRUE(WIFEXITED(ws));
ASSERT_EQ(42, WEXITSTATUS(ws));
}
void vfork_wait_in_serial(void) {
int pid, ws;
ASSERT_NE(-1, (pid = vfork()));
if (!pid)
@ -162,7 +179,7 @@ void VforkInSerial(void) {
ASSERT_EQ(0, WEXITSTATUS(ws));
}
void SysForkInSerial(void) {
void sys_fork_wait_in_serial(void) {
int pid, ws;
ASSERT_NE(-1, (pid = sys_fork()));
if (!pid)
@ -172,11 +189,31 @@ void SysForkInSerial(void) {
ASSERT_EQ(0, WEXITSTATUS(ws));
}
TEST(fork, bench) {
VforkInSerial();
BENCHMARK(10, 1, VforkInSerial());
if (!IsWindows())
BENCHMARK(10, 1, SysForkInSerial());
ForkInSerial();
BENCHMARK(10, 1, ForkInSerial());
void posix_spawn_in_serial(void) {
int ws, pid;
char *prog = "./life";
char *args[] = {prog, NULL};
char *envs[] = {NULL};
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
ASSERT_NE(-1, waitpid(pid, &ws, 0));
ASSERT_TRUE(WIFEXITED(ws));
ASSERT_EQ(42, WEXITSTATUS(ws));
}
TEST(fork, bench) {
if (IsWindows()) {
testlib_extract("/zip/life-pe.ape", "life", 0755);
} else {
testlib_extract("/zip/life", "life", 0755);
}
vfork_wait_in_serial();
vfork_execl_wait_in_serial();
posix_spawn_in_serial();
BENCHMARK(10, 1, vfork_wait_in_serial());
if (!IsWindows())
BENCHMARK(10, 1, sys_fork_wait_in_serial());
fork_wait_in_serial();
BENCHMARK(10, 1, fork_wait_in_serial());
BENCHMARK(10, 1, posix_spawn_in_serial());
BENCHMARK(10, 1, vfork_execl_wait_in_serial());
}

3
test/libc/proc/life.c Normal file
View file

@ -0,0 +1,3 @@
int main(int argc, char *argv[]) {
return 42;
}

View file

@ -90,7 +90,6 @@ __attribute__((__constructor__)) static void init(void) {
}
}
#ifdef __x86_64__
TEST(sched_setaffinity, isInheritedAcrossExecve) {
cpu_set_t x;
CPU_ZERO(&x);
@ -105,7 +104,6 @@ TEST(sched_setaffinity, isInheritedAcrossExecve) {
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(42, WEXITSTATUS(ws));
}
#endif /* __x86_64__ */
TEST(sched_getaffinity, getpid) {
cpu_set_t x, y;

View file

@ -18,8 +18,8 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/intrin/fds.h"
#include "libc/dce.h"
#include "libc/intrin/fds.h"
#include "libc/intrin/kprintf.h"
#include "libc/nt/winsock.h"
#include "libc/runtime/runtime.h"
@ -141,8 +141,6 @@ TEST(socket, canBeInheritedByForkedWorker) {
WAIT(exit, 0);
}
#ifdef __x86_64__
__attribute__((__constructor__)) static void StdioPro(int argc, char *argv[]) {
if (argc >= 2 && !strcmp(argv[1], "StdioProg")) {
ASSERT_EQ(NULL, getenv("__STDIO_SOCKETS"));
@ -184,5 +182,3 @@ TEST(socket, canBeUsedAsExecutedStdio) {
EXPECT_SYS(0, 0, close(3));
WAIT(exit, 0);
}
#endif /* __x86_64__ */

View file

@ -30,9 +30,9 @@ TEST_LIBC_SYSTEM_DIRECTDEPS = \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_PROC \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_STDIO \
LIBC_STR \
LIBC_SYSTEM \
LIBC_SYSV \
@ -82,6 +82,21 @@ o/$(MODE)/test/libc/system/system_test.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/system/trace_test.dbg: \
$(TEST_LIBC_SYSTEM_DEPS) \
o/$(MODE)/test/libc/system/trace_test.o \
o/$(MODE)/test/libc/system/system.pkg \
o/$(MODE)/test/libc/system/popen_test.zip.o \
o/$(MODE)/test/libc/system/popen_test.dbg.zip.o \
o/$(MODE)/tool/build/echo.zip.o \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/system/popen_test.zip.o: private ZIPOBJ_FLAGS += -B
o/$(MODE)/test/libc/system/popen_test.dbg.zip.o: private ZIPOBJ_FLAGS += -B
$(TEST_LIBC_SYSTEM_OBJS): test/libc/system/BUILD.mk
.PHONY: o/$(MODE)/test/libc/system

View file

@ -27,10 +27,9 @@
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/benchmark.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#ifdef __x86_64__
#define GETEXITSTATUS(x) \
({ \
@ -276,15 +275,9 @@ TEST(system, pipelineCanOutputBackToSelf) {
RestoreStdout();
}
int system2(const char *);
BENCH(system, bench) {
TEST(system, bench) {
testlib_extract("/zip/echo", "echo", 0755);
EZBENCH2("system cmd", donothing, system("./echo hi >/dev/null"));
EZBENCH2("systemvpe cmd", donothing,
systemvpe("./echo", (char *[]){"./echo", "hi", 0}, 0));
EZBENCH2("cocmd echo", donothing, system("echo hi >/dev/null"));
EZBENCH2("cocmd exit", donothing, system("exit"));
BENCHMARK(10, 1, system("./echo hi >/dev/null"));
BENCHMARK(10, 1, system("echo hi >/dev/null"));
BENCHMARK(10, 1, system("exit"));
}
#endif /* __x86_64__ */

View file

@ -0,0 +1,74 @@
/*-*- 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 2024 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/assert.h"
#include "libc/calls/calls.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
// make sure that running `popen_test --ftrace --strace` doesn't crash
//
// function and system call tracing are invasive runtime features that
// can easily break if interrupting the other magical, deeply embedded
// parts of the runtime, like mutations to the rbtree ftrace needs for
// validating stack pointers (kisdangerous() locks the mmap lock), and
// that's why we use dontinstrument in so many places in the codebase.
//
// we like popen_test because it tests the intersection of forking and
// threads, and it activates other subsystems like the signal / itimer
// worker threads on windows. if we can ftrace and strace it, then you
// can be assured cosmo's tracing support works right on all platforms
void SetUpOnce(void) {
testlib_enable_tmp_setup_teardown();
}
TEST(trace, test) {
unsetenv("MAKEFLAGS"); // avoid testmain.c 254 status
testlib_extract("/zip/popen_test", "popen_test", 0755);
testlib_extract("/zip/popen_test.dbg", "popen_test.dbg", 0755);
if (!fork()) {
close(1);
close(2);
open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
dup(1);
execl("./popen_test", "./popen_test", "--ftrace", "--strace", NULL);
_Exit(128);
}
int ws;
unassert(wait(&ws));
if (WIFSIGNALED(ws)) {
fprintf(stderr,
"%s:%d: error: trace_test got %s signal running "
"popen_test --strace --ftrace (see %s for output)\n",
__FILE__, __LINE__, strsignal(WTERMSIG(ws)), realpath("log", 0));
_Exit(1);
}
if (WEXITSTATUS(ws)) {
fprintf(stderr,
"%s:%d: error: trace_test got %d exit status running "
"popen_test --strace --ftrace (see %s for output)\n",
__FILE__, __LINE__, WEXITSTATUS(ws), realpath("log", 0));
_Exit(1);
}
}

View file

@ -39,7 +39,6 @@
#include "libc/testlib/testlib.h"
#include "libc/x/x.h"
#include "third_party/regex/regex.h"
#ifdef __x86_64__
__static_yoink("zipos");
__static_yoink("o/" MODE "/test/tool/net/redbean-tester");
@ -292,5 +291,3 @@ Z\n",
EXPECT_NE(-1, wait(0));
EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0));
}
#endif /* __x86_64__ */

View file

@ -15,7 +15,7 @@
// It is expected to invalidate the instruction cache for the
// specified range.
void __clear_cache(void *start, void *end) {
privileged void __clear_cache(void *start, void *end) {
#ifdef __aarch64__
if (IsXnu()) {
@ -59,6 +59,8 @@ void __clear_cache(void *start, void *end) {
}
__asm__ volatile("isync");
#elif defined(__x86_64__)
// do nothing
#else
compilerrt_abort();
#endif

View file

@ -110,6 +110,8 @@ uint32_t nsync_spin_test_and_set_ (nsync_atomic_uint32_ *w, uint32_t test,
/* ====================================================================================== */
#if NSYNC_DEBUG
struct nsync_waiter_s *nsync_dll_nsync_waiter_ (struct Dll *e) {
struct nsync_waiter_s *nw = DLL_CONTAINER(struct nsync_waiter_s, q, e);
ASSERT (nw->tag == NSYNC_WAITER_TAG);
@ -133,6 +135,8 @@ waiter *nsync_dll_waiter_samecond_ (struct Dll *e) {
return (w);
}
#endif /* NSYNC_DEBUG */
/* -------------------------------- */
// TODO(jart): enforce in dbg mode once off-by-one flake is fixed
@ -249,8 +253,10 @@ static bool free_waiters_populate (void) {
return (false);
for (size_t i = 0; i < n; ++i) {
waiter *w = &waiters[i];
#if NSYNC_DEBUG
w->tag = WAITER_TAG;
w->nw.tag = NSYNC_WAITER_TAG;
#endif
if (!nsync_mu_semaphore_init (&w->sem)) {
if (!i) {
// netbsd can run out of semaphores
@ -327,18 +333,26 @@ void nsync_waiter_wipe_ (void) {
nsync_mu_semaphore_destroy (&w->sem);
for (w = wall; w; w = next) {
next = w->next_all;
w->tag = 0;
w->flags = 0;
w->nw.tag = 0;
#if NSYNC_DEBUG
w->tag = WAITER_TAG;
w->nw.tag = NSYNC_WAITER_TAG;
#endif
w->nw.flags = NSYNC_WAITER_FLAG_MUCV;
atomic_init(&w->nw.waiting, 0);
w->l_type = 0;
bzero (&w->cond, sizeof (w->cond));
w->cond.f = 0;
w->cond.v = 0;
w->cond.eq = 0;
dll_init (&w->same_condition);
if (w->wipe_mu)
bzero (w->wipe_mu, sizeof (*w->wipe_mu));
if (w->wipe_cv)
bzero (w->wipe_cv, sizeof (*w->wipe_cv));
if (w->wipe_mu) {
atomic_init(&w->wipe_mu->word, 0);
w->wipe_mu->waiters = 0;
}
if (w->wipe_cv) {
atomic_init(&w->wipe_cv->word, 0);
w->wipe_cv->waiters = 0;
}
if (!nsync_mu_semaphore_init (&w->sem))
continue; /* leak it */
w->next_free = prev;

View file

@ -9,15 +9,10 @@
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/note.h"
#include "third_party/nsync/time.h"
#include "third_party/nsync/defs.h"
#include "third_party/nsync/wait_s.internal.h"
COSMOPOLITAN_C_START_
#ifdef MODE_DBG
#define NSYNC_DEBUG 1
#else
#define NSYNC_DEBUG 0
#endif
/* Yield the CPU. Platform specific. */
void nsync_yield_(void);
@ -191,13 +186,15 @@ struct wait_condition_s {
ATM_STORE_REL (&w.waiting, 0);
nsync_mu_semaphore_v (&w.sem); */
typedef struct waiter_s {
#if NSYNC_DEBUG
uint32_t tag; /* Debug DLL_NSYNC_WAITER, DLL_WAITER, DLL_WAITER_SAMECOND. */
#endif
int flags; /* See WAITER_* bits below. */
nsync_atomic_uint32_ remove_count; /* Monotonic count of removals from queue. */
nsync_semaphore sem; /* Thread waits on this semaphore. */
struct nsync_waiter_s nw; /* An embedded nsync_waiter_s. */
struct nsync_mu_s_ *cv_mu; /* Pointer to nsync_mu associated with a cv wait. */
lock_type *l_type; /* Lock type of the mu, or nil if not associated with a mu. */
nsync_atomic_uint32_ remove_count; /* Monotonic count of removals from queue. */
struct wait_condition_s cond; /* A condition on which to acquire a mu. */
struct Dll same_condition; /* Links neighbours in nw.q with same non-nil condition. */
struct waiter_s * next_all;

12
third_party/nsync/defs.h vendored Normal file
View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_
#define COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_
COSMOPOLITAN_C_START_
#ifdef MODE_DBG
#define NSYNC_DEBUG 1
#else
#define NSYNC_DEBUG 0
#endif
COSMOPOLITAN_C_END_
#endif /* COSMOPOLITAN_THIRD_PARTY_NSYNC_DEFS_H_ */

View file

@ -20,6 +20,7 @@
#include "third_party/nsync/common.internal.h"
#include "third_party/nsync/mu_semaphore.h"
#include "third_party/nsync/races.internal.h"
#include "third_party/nsync/defs.h"
#include "third_party/nsync/wait_s.internal.h"
__static_yoink("nsync_notice");
@ -148,15 +149,23 @@ static void emit_waiters (struct emit_buf *b, struct Dll *list) {
waiter *w = DLL_WAITER (p);
next = NULL;
emit_print (b, " %i", (uintptr_t) w);
#if NSYNC_DEBUG
if (w->tag != WAITER_TAG) {
emit_print (b, "bad WAITER_TAG %i",
(uintptr_t) w->tag);
} else {
#else
{
#endif
next = dll_next (list, p);
#if NSYNC_DEBUG
if (nw->tag != NSYNC_WAITER_TAG) {
emit_print (b, " bad WAITER_TAG %i",
(uintptr_t) nw->tag);
} else {
#else
{
#endif
emit_print (b, " embedded=%i waiting=%i",
(uintptr_t) (w->flags & NSYNC_WAITER_FLAG_MUCV),
(uintptr_t) ATM_LOAD (&nw->waiting));

View file

@ -40,7 +40,9 @@ int nsync_sem_wait_with_cancel_ (waiter *w, int clock, nsync_time abs_deadline,
sem_outcome = ECANCELED;
if (nsync_time_cmp (cancel_time, nsync_time_zero) > 0) {
struct nsync_waiter_s nw;
#if NSYNC_DEBUG
nw.tag = NSYNC_WAITER_TAG;
#endif
nw.sem = &w->sem;
dll_init (&nw.q);
ATM_STORE (&nw.waiting, 1);

View file

@ -51,7 +51,9 @@ int nsync_wait_n (void *mu, void (*lock) (void *), void (*unlock) (void *),
nw = (struct nsync_waiter_s *) malloc (count * sizeof (nw[0]));
}
for (i = 0; i != count && enqueued; i++) {
#if NSYNC_DEBUG
nw[i].tag = NSYNC_WAITER_TAG;
#endif
nw[i].sem = &w->sem;
dll_init (&nw[i].q);
ATM_STORE (&nw[i].waiting, 0);

View file

@ -48,7 +48,6 @@ COSMOPOLITAN_C_START_
*/
typedef struct nsync_mu_s_ {
nsync_atomic_uint32_ word; /* internal use only */
int _zero; /* c pthread_mutex_t */
struct Dll *waiters; /* internal use only */
} nsync_mu;

View file

@ -60,7 +60,7 @@ typedef struct cv_stress_data_s {
/* The delays in cv_stress_inc_loop(), cv_stress_reader_loop(), mu_stress_inc_loop(),
and mu_stress_reader_loop() are uniformly distributed from 0 to
STRESS_MAX_DELAY_MICROS-1 microseconds. */
#define STRESS_MAX_DELAY_MICROS (IsNetbsd() || IsOpenbsd() ? 20000 : 4000) /* maximum delay */
#define STRESS_MAX_DELAY_MICROS (IsNetbsd() || IsOpenbsd() ? 30000 : 4000) /* maximum delay */
#define STRESS_MEAN_DELAY_MICROS (STRESS_MAX_DELAY_MICROS / 2) /* mean delay */
#define STRESS_EXPECT_TIMEOUTS_PER_SEC (1000000 / STRESS_MEAN_DELAY_MICROS) /* expect timeouts/s*/

View file

@ -20,6 +20,7 @@
#include "third_party/nsync/testing/smprintf.h"
#include "third_party/nsync/testing/testing.h"
#include "third_party/nsync/testing/time_extra.h"
#include "libc/dce.h"
#include "third_party/nsync/time.h"
/* Verify the properties of a prenotified note. */
@ -78,7 +79,7 @@ static void test_note_unnotified (testing t) {
TEST_ERROR (t, ("timed wait on unnotified note returned too quickly (1s wait took %s)",
nsync_time_str (waited, 2)));
}
if (nsync_time_cmp (waited, nsync_time_ms (2000)) > 0) {
if (nsync_time_cmp (waited, nsync_time_ms (IsNetbsd() || IsOpenbsd() || IsFreebsd() ? 4000 : 2000)) > 0) {
TEST_ERROR (t, ("timed wait on unnotified note returned too slowly (1s wait took %s)",
nsync_time_str (waited, 2)));
}

View file

@ -1,6 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_
#define COSMOPOLITAN_LIBC_THREAD_WAIT_INTERNAL_H_
#include "libc/intrin/dll.h"
#include "third_party/nsync/defs.h"
#include "third_party/nsync/atomic.h"
COSMOPOLITAN_C_START_
@ -10,10 +11,12 @@ COSMOPOLITAN_C_START_
with v pointing to the client's object and nw pointing to a struct
nsync_waiter_s. */
struct nsync_waiter_s {
#if NSYNC_DEBUG
uint32_t tag; /* used for debugging */
#endif
uint32_t flags; /* see below */
struct Dll q; /* used to link children of parent */
nsync_atomic_uint32_ waiting; /* non-zero <=> the waiter is waiting */
struct Dll q; /* used to link children of parent */
struct nsync_semaphore_s_ *sem; /* *sem will be Ved when waiter is woken */
};

View file

@ -94,4 +94,6 @@ o/$(MODE)/tool/hello/wait-pe.ape: \
o/$(MODE)/tool/build/elf2pe
@$(COMPILE) -AELF2PE o/$(MODE)/tool/build/elf2pe -R 64kb -S 4kb -o $@ $<
o/$(MODE)/tool/hello/life-pe.ape.zip.o: private ZIPOBJ_FLAGS += -B
$(TOOL_HELLO_OBJS): tool/hello/BUILD.mk