Add torture test for zipos file descriptors

This change hardens the code for opening /zip/ files using the system
call interface. Thread safety and signal safety has been improved for
file descriptors in general. We now document fixed addresses that are
needed for low level allocations.
This commit is contained in:
Justine Tunney 2022-06-15 16:19:50 -07:00
parent 579080cd4c
commit e466dd0553
44 changed files with 2981 additions and 307 deletions

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
@ -39,11 +40,9 @@
* that doesn't mean the error should be ignored.
*
* @return 0 on success, or -1 w/ errno
* @error EINTR means a signal was received while closing (possibly
* because linger is enabled) in which case close() does not need to
* be called again, since the fd will close in the background, and
* chances are that on linux, the fd is already closed, even if the
* underlying resource isn't closed yet
* @error EINTR means a signal was received while closing in which case
* close() does not need to be called again, since the fd will close
* in the background
* @asyncsignalsafe
* @vforksafe
*/
@ -54,6 +53,11 @@ int close(int fd) {
} else if (fd < 0) {
rc = einval();
} else {
// for performance reasons we want to avoid holding __fds_lock()
// while sys_close() is happening. this leaves the kernel / libc
// having a temporarily inconsistent state. routines that obtain
// file descriptors the way __zipos_open() does need to retry if
// there's indication this race condition happened.
if (__isfdkind(fd, kFdZip)) {
rc = weaken(__zipos_close)(fd);
} else {
@ -71,8 +75,7 @@ int close(int fd) {
__isfdkind(fd, kFdProcess)) { //
rc = sys_close_nt(g_fds.p + fd);
} else {
STRACE("close(%d) unknown kind", fd);
rc = ebadf();
rc = eio();
}
}
}

View file

@ -28,9 +28,10 @@
* @param mode is an octal user/group/other permission signifier, that's
* ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno
* @asyncsignalsafe
* @asyncsignalsafe (zip files may have issues)
* @vforksafe (raises error if zip file)
* @restartable
* @vforksafe
* @threadsafe
*/
int open(const char *file, int flags, ...) {
va_list va;

View file

@ -45,8 +45,9 @@
* @param mode is an octal user/group/other permission signifier, that's
* ignored if O_CREAT or O_TMPFILE weren't passed
* @return number needing close(), or -1 w/ errno
* @asyncsignalsafe
* @vforksafe
* @asyncsignalsafe (zip files may have issues)
* @vforksafe (raises error if zip file)
* @threadsafe
*/
int openat(int dirfd, const char *file, int flags, ...) {
int rc;

View file

@ -22,51 +22,65 @@
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
// XXX: until we can add read locks to all the code that uses g_fds.p
// (right now we only have write locks) we need to keep old copies
// of g_fds.p around after it's been extended, so that threads
// which are using an fd they de facto own can continue reading
static void FreeOldFdsArray(void *p) {
weaken(free)(p);
}
static volatile size_t mapsize;
/**
* Grows file descriptor array memory if needed.
*
* @see libc/runtime/memtrack64.txt
* @see libc/runtime/memtrack32.txt
* @asyncsignalsafe
*/
int __ensurefds_unlocked(int fd) {
size_t n1, n2;
struct Fd *p1, *p2;
uint64_t addr;
int prot, flags;
size_t size, chunk;
struct DirectMap dm;
if (fd < g_fds.n) return fd;
STRACE("__ensurefds(%d) extending", fd);
if (!weaken(malloc)) return emfile();
p1 = g_fds.p;
n1 = g_fds.n;
if (p1 == g_fds.__init_p) {
if (!(p2 = weaken(malloc)(sizeof(g_fds.__init_p)))) return -1;
memcpy(p2, p1, sizeof(g_fds.__init_p));
g_fds.p = p1 = p2;
size = mapsize;
chunk = FRAMESIZE;
if (IsAsan()) chunk *= 8;
addr = kMemtrackFdsStart + size;
prot = PROT_READ | PROT_WRITE;
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
dm = sys_mmap((char *)addr, chunk, prot, flags, -1, 0);
TrackMemoryInterval(&_mmi, addr >> 16, (addr + chunk - 1) >> 16, dm.maphandle,
prot, flags, false, false, 0, chunk);
if (IsAsan()) {
addr = (addr >> 3) + 0x7fff8000;
dm = sys_mmap((char *)addr, FRAMESIZE, prot, flags, -1, 0);
TrackMemoryInterval(&_mmi, addr >> 16, addr >> 16, dm.maphandle, prot,
flags, false, false, 0, FRAMESIZE);
}
n2 = n1;
while (n2 <= fd) n2 *= 2;
if (!(p2 = weaken(malloc)(n2 * sizeof(*p1)))) return -1;
__cxa_atexit(FreeOldFdsArray, p1, 0);
memcpy(p2, p1, n1 * sizeof(*p1));
bzero(p2 + n1, (n2 - n1) * sizeof(*p1));
g_fds.p = p2;
g_fds.n = n2;
if (!size) {
g_fds.p = memcpy((char *)kMemtrackFdsStart, g_fds.__init_p,
sizeof(g_fds.__init_p));
}
g_fds.n = (size + chunk) / sizeof(*g_fds.p);
mapsize = size + chunk;
return fd;
}
/**
* Grows file descriptor array memory if needed.
* @asyncsignalsafe
* @threadsafe
*/
int __ensurefds(int fd) {
__fds_lock();
@ -77,22 +91,29 @@ int __ensurefds(int fd) {
/**
* Finds open file descriptor slot.
* @asyncsignalsafe
*/
int __reservefd_unlocked(int start) {
int fd;
for (fd = MAX(start, g_fds.f); fd < g_fds.n; ++fd) {
if (!g_fds.p[fd].kind) {
break;
for (;;) {
for (fd = MAX(start, g_fds.f); fd < g_fds.n; ++fd) {
if (!g_fds.p[fd].kind) {
break;
}
}
fd = __ensurefds_unlocked(fd);
bzero(g_fds.p + fd, sizeof(*g_fds.p));
if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) {
_cmpxchg(&g_fds.f, fd, fd + 1);
return fd;
}
}
fd = __ensurefds_unlocked(fd);
bzero(g_fds.p + fd, sizeof(*g_fds.p));
g_fds.p[fd].kind = kFdReserved;
return fd;
}
/**
* Finds open file descriptor slot.
* @asyncsignalsafe
* @threadsafe
*/
int __reservefd(int start) {
int fd;
@ -101,37 +122,3 @@ int __reservefd(int start) {
__fds_unlock();
return fd;
}
/**
* Closes non-stdio file descriptors to free dynamic memory.
*/
static void FreeFds(void) {
int i, keep = 3;
STRACE("FreeFds()");
__fds_lock();
for (i = keep; i < g_fds.n; ++i) {
if (g_fds.p[i].kind) {
__fds_unlock();
close(i);
__fds_lock();
}
}
if (g_fds.p != g_fds.__init_p) {
bzero(g_fds.__init_p, sizeof(g_fds.__init_p));
memcpy(g_fds.__init_p, g_fds.p, sizeof(*g_fds.p) * keep);
if (weaken(free)) {
weaken(free)(g_fds.p);
}
g_fds.p = g_fds.__init_p;
g_fds.n = ARRAYLEN(g_fds.__init_p);
}
__fds_unlock();
}
static textstartup void FreeFdsInit(void) {
atexit(FreeFds);
}
const void *const FreeFdsCtor[] initarray = {
FreeFdsInit,
};

View file

@ -21,7 +21,7 @@
/**
* Converts unsigned 64-bit integer to string w/ commas.
*
* @param p needs at least 21 bytes
* @param p needs at least 27 bytes
* @return pointer to nul byte
*/
dontinline char *FormatUint64Thousands(char p[static 27], uint64_t x) {
@ -44,7 +44,7 @@ dontinline char *FormatUint64Thousands(char p[static 27], uint64_t x) {
/**
* Converts 64-bit integer to string w/ commas.
*
* @param p needs at least 21 bytes
* @param p needs at least 27 bytes
* @return pointer to nul byte
*/
char *FormatInt64Thousands(char p[static 27], int64_t x) {

View file

@ -0,0 +1,54 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/itoa.h"
#include "libc/macros.internal.h"
static const struct {
char suffix;
uint64_t size;
} kUnits[] = {
{'e', 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024},
{'p', 1024ULL * 1024 * 1024 * 1024 * 1024},
{'t', 1024ULL * 1024 * 1024 * 1024},
{'g', 1024ULL * 1024 * 1024},
{'m', 1024ULL * 1024},
{'k', 1024ULL},
};
/**
* Represents size of memory readably.
*
* @param p is output buffer
* @return pointer to nul byte
*/
char *FormatMemorySize(char *p, uint64_t x) {
int i, suffix;
for (suffix = i = 0; i < ARRAYLEN(kUnits); ++i) {
if (x >= kUnits[i].size * 9) {
x = (x + kUnits[i].size / 2) / kUnits[i].size;
suffix = kUnits[i].suffix;
break;
}
}
p = FormatUint64(p, x);
if (suffix) *p++ = suffix;
*p++ = 'b';
*p = 0;
return p;
}

View file

@ -21,6 +21,7 @@ char *FormatFlex64(char[hasatleast 24], int64_t, char);
size_t uint64toarray_radix16(uint64_t, char[hasatleast 17]);
size_t uint64toarray_fixed16(uint64_t, char[hasatleast 17], uint8_t);
size_t uint64toarray_radix8(uint64_t, char[hasatleast 24]);
char *FormatMemorySize(char *, uint64_t);
#ifndef __STRICT_ANSI__
size_t int128toarray_radix10(int128_t, char *);

View file

@ -1210,38 +1210,26 @@ void __asan_unregister_globals(struct AsanGlobal g[], int n) {
}
}
void __asan_evil(uint8_t *addr, int size, const char *s1, const char *s2) {
void __asan_evil(uint8_t *addr, int size, const char *s) {
struct AsanTrace tr;
__asan_rawtrace(&tr, __builtin_frame_address(0));
kprintf(
"WARNING: ASAN %s %s bad %d byte %s at %x bt %x %x %x\n",
__asan_noreentry == gettid() ? "error during" : "multi-threaded crash",
s1, size, s2, addr, tr.p[0], tr.p[1], tr.p[2], tr.p[3]);
kprintf("WARNING: ASAN bad %d byte %s at %x bt %x %x %x\n", size, s, addr,
tr.p[0], tr.p[1], tr.p[2], tr.p[3]);
}
void __asan_report_load(uint8_t *addr, int size) {
if (_lockcmpxchg(&__asan_noreentry, 0, gettid())) {
if (!__vforked) {
__asan_report_memory_fault(addr, size, "load")();
__asan_unreachable();
} else {
__asan_evil(addr, size, "vfork()", "load");
}
} else {
__asan_evil(addr, size, "ASAN Reporting", "load");
__asan_evil(addr, size, "load");
if (!__vforked && _lockcmpxchg(&__asan_noreentry, 0, 1)) {
__asan_report_memory_fault(addr, size, "load")();
__asan_unreachable();
}
}
void __asan_report_store(uint8_t *addr, int size) {
if (_lockcmpxchg(&__asan_noreentry, 0, gettid())) {
if (!__vforked) {
__asan_report_memory_fault(addr, size, "store")();
__asan_unreachable();
} else {
__asan_evil(addr, size, "vfork()", "store");
}
} else {
__asan_evil(addr, size, "ASAN reporting", "store");
__asan_evil(addr, size, "store");
if (!__vforked && _lockcmpxchg(&__asan_noreentry, 0, 1)) {
__asan_report_memory_fault(addr, size, "store")();
__asan_unreachable();
}
}

View file

@ -41,6 +41,7 @@ textstartup void InitializeFileDescriptors(void) {
struct Fds *fds;
fds = VEIL("r", &g_fds);
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
fds->f = 3;
fds->p = fds->__init_p;
if (IsMetal()) {
pushmov(&fds->f, 3ull);

View file

@ -21,9 +21,8 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/nexgen32e/gettls.h"
#include "libc/linux/futex.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/sysv/consts/futex.h"
/**
* Locks mutex.
@ -43,16 +42,15 @@ int pthread_mutex_lock(pthread_mutex_t *mutex) {
return EDEADLK;
}
}
atomic_fetch_add(&mutex->waits, +1);
if (!IsLinux() ||
sys_futex((void *)&mutex->owner, FUTEX_WAIT, owner, 0, 0)) {
atomic_fetch_add(&mutex->waits, 1);
if (!IsLinux() || LinuxFutexWait((void *)&mutex->owner, owner, 0)) {
if (++tries & 7) {
__builtin_ia32_pause();
} else {
sched_yield();
}
}
atomic_fetch_add(&mutex->waits, -1);
atomic_fetch_sub(&mutex->waits, 1);
}
++mutex->reent;
return 0;

View file

@ -21,8 +21,8 @@
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/pthread.h"
#include "libc/linux/futex.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/sysv/consts/futex.h"
/**
* Releases mutex.
@ -38,7 +38,7 @@ int pthread_mutex_unlock(pthread_mutex_t *mutex) {
atomic_store_explicit(&mutex->owner, 0, memory_order_relaxed);
if (IsLinux() &&
atomic_load_explicit(&mutex->waits, memory_order_acquire)) {
sys_futex((void *)&mutex->owner, FUTEX_WAKE, 1, 0, 0);
LinuxFutexWake(&mutex->owner, 1);
}
}
return 0;

View file

@ -18,10 +18,11 @@
*/
#include "libc/calls/internal.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
void __releasefd_unlocked(int fd) {
if (0 <= fd && fd < g_fds.n) {
g_fds.p[fd].kind = 0;
bzero(g_fds.p + fd, sizeof(*g_fds.p));
g_fds.f = MIN(fd, g_fds.f);
}
}

View file

@ -61,18 +61,30 @@ privileged void *__initialize_tls(char tib[64]) {
* Installs thread information block on main process.
*
* For example, to set up TLS correctly for the main thread, without
* creating any threads using `clone` (which does this automatically),
* it is sufficient to say:
* creating any threads, then it's sufficient to say:
*
* __attribute__((__constructor__)) static void InitTls(void) {
* static char tls[64];
* __initialize_tls(tls);
* __threaded = *(int *)(tls + 0x38) = gettid();
* *(int *)(tls + 0x38) = gettid();
* *(int *)(tls + 0x3c) = __errno;
* __install_tls(tls);
* }
*
* Since that'll ensure it happens exactly once.
* We use a constructor here to make sure it only happens once. Please
* note that calling `clone` will do this automatically.
*
* Installing TLS causes the `__tls_enabled` variable to be set. This
* causes C library features such as `errno` and `gettid()` to use TLS.
* This can help things like recursive mutexes go significantly faster.
*
* To access your TLS storage, you can call `__get_tls()` or
* __get_tls_inline()` which return the address of the `tib`.
*
* @param tib is your thread information block, which must have at least
* 64 bytes on the righthand side of the tib pointer since those are
* the values your C library reserves for itself. memory on the left
* side of the pointer is reserved by the linker for _Thread_local.
*/
privileged void __install_tls(char tib[64]) {
int ax, dx;

27
libc/linux/futex.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef COSMOPOLITAN_LIBC_LINUX_FUTEX_H_
#define COSMOPOLITAN_LIBC_LINUX_FUTEX_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
#include "libc/calls/struct/timespec.h"
forceinline int LinuxFutexWait(void *addr, int expect,
struct timespec *timeout) {
int ax;
register void *r10 asm("r10") = timeout;
asm volatile("syscall"
: "=a"(ax)
: "0"(202), "D"(addr), "S"(0), "d"(expect), "r"(r10)
: "rcx", "r11", "memory");
return ax;
}
forceinline int LinuxFutexWake(void *addr, int count) {
int ax;
asm volatile("syscall"
: "=a"(ax)
: "0"(202), "D"(addr), "S"(1), "d"(count)
: "rcx", "r11", "memory");
return ax;
}
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_LINUX_FUTEX_H_ */

View file

@ -21,7 +21,9 @@
#include "libc/bits/safemacros.internal.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -82,6 +84,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
}
// backtrace_test.com failing on windows for some reason via runitd
// don't want to pull in the high-level syscalls here anyway
if (IsWindows()) {
return -1;
}
@ -118,17 +121,17 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
j += uint64toarray_radix16(addr - 1, buf + j) + 1;
}
argv[i++] = NULL;
pipe(pipefds);
if (sys_pipe(pipefds) == -1) return -1;
if (!(pid = vfork())) {
dup2(pipefds[1], 1);
if (pipefds[0] != 1) close(pipefds[0]);
if (pipefds[1] != 1) close(pipefds[1]);
execve(addr2line, argv, environ);
_exit(127);
sys_dup2(pipefds[1], 1);
if (pipefds[0] != 1) sys_close(pipefds[0]);
if (pipefds[1] != 1) sys_close(pipefds[1]);
sys_execve(addr2line, argv, environ);
_Exit(127);
}
close(pipefds[1]);
sys_close(pipefds[1]);
for (;;) {
got = read(pipefds[0], buf, kBacktraceBufSize);
got = sys_read(pipefds[0], buf, kBacktraceBufSize);
if (!got) break;
if (got == -1 && errno == EINTR) {
errno = 0;
@ -161,8 +164,8 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) {
}
}
}
close(pipefds[0]);
while (waitpid(pid, &ws, 0) == -1) {
sys_close(pipefds[0]);
while (sys_wait4(pid, &ws, 0, 0) == -1) {
if (errno == EINTR) continue;
return -1;
}

View file

@ -20,7 +20,6 @@
#include "libc/bits/atomic.h"
#include "libc/bits/weaken.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/spinlock.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
@ -71,14 +70,14 @@ static struct Memlog {
long usage;
} __memlog;
_Alignas(64) static int __memlog_lock_obj;
static pthread_mutex_t __memlog_lock_obj;
static void __memlog_lock(void) {
_spinlock(&__memlog_lock_obj);
pthread_mutex_lock(&__memlog_lock_obj);
}
static void __memlog_unlock(void) {
_spunlock(&__memlog_lock_obj);
pthread_mutex_unlock(&__memlog_lock_obj);
}
static long __memlog_size(void *p) {

View file

@ -263,8 +263,7 @@ static wontreturn relegated noinstrument void __minicrash(int sig,
*
* This function never returns, except for traps w/ human supervision.
*/
relegated noinstrument void __oncrash(int sig, struct siginfo *si,
ucontext_t *ctx) {
relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
intptr_t rip;
int gdbpid, err;
static bool noreentry, notpossible;

View file

@ -30,6 +30,9 @@
* called, then it'll return the same sequence each time your program
* runs. Faster and more modern alternatives exist to this function.
*
* This function is not thread safe in the sense that multiple threads
* might simultaneously generate the same random values.
*
* @note this function does well on bigcrush and practrand
* @note this function is not intended for cryptography
* @see lemur64(), rand64(), rdrand()

View file

@ -16,42 +16,44 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pthread.h"
#include "libc/intrin/spinlock.h"
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"
static int thepid;
static uint128_t thepool;
_Alignas(64) static int rand64_lock;
static struct {
int thepid;
uint128_t thepool;
pthread_mutex_t lock;
} g_rand64;
/**
* Returns nondeterministic random data.
*
* This function is similar to lemur64() except that it's intended to be
* unpredictable. This PRNG automatically seeds itself on startup using
* a much stronger and faster random source than `srand(time(0))`. This
* function will automatically reseed itself when new processes and
* threads are spawned. This function is thread safe in the sense that a
* race condition can't happen where two threads return the same result.
* This function is similar to lemur64() except that it doesn't produce
* the same sequences of numbers each time your program is run. This is
* the case even across forks and threads, whose sequences will differ.
*
* @see rdseed(), rdrand(), rand(), random(), rngset()
* @note this function takes 5 cycles (30 if `__threaded`)
* @note this function is not intended for cryptography
* @note this function passes bigcrush and practrand
* @note this function takes at minimum 15 cycles
* @asyncsignalsafe
* @threadsafe
* @vforksafe
*/
uint64_t rand64(void) {
void *p;
uint128_t s;
_spinlock(&rand64_lock);
if (__pid == thepid) {
s = thepool; // normal path
if (__threaded) pthread_mutex_lock(&g_rand64.lock);
if (__pid == g_rand64.thepid) {
s = g_rand64.thepool; // normal path
} else {
if (!thepid) {
if (!g_rand64.thepid) {
if (AT_RANDOM && (p = (void *)getauxval(AT_RANDOM))) {
// linux / freebsd kernel supplied entropy
memcpy(&s, p, 16);
@ -61,13 +63,13 @@ uint64_t rand64(void) {
}
} else {
// blend another timestamp on fork contention
s = thepool ^ rdtsc();
s = g_rand64.thepool ^ rdtsc();
}
// blend the pid on startup and fork contention
s ^= __pid;
thepid = __pid;
g_rand64.thepid = __pid;
}
thepool = (s *= 15750249268501108917ull); // lemur64
_spunlock(&rand64_lock);
g_rand64.thepool = (s *= 15750249268501108917ull); // lemur64
if (__threaded) pthread_mutex_unlock(&g_rand64.lock);
return s >> 64;
}

View file

@ -30,6 +30,7 @@
#include "libc/nt/runtime.h"
#include "libc/nt/thread.h"
#include "libc/nt/thunk/msabi.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/clone.h"
@ -72,34 +73,6 @@ struct CloneArgs {
void *arg;
};
////////////////////////////////////////////////////////////////////////////////
// THREADING RUNTIME
static char tibdefault[64];
extern int __threadcalls_end[];
extern int __threadcalls_start[];
static privileged dontinline void FixupThreadCalls(void) {
/*
* _NOPL("__threadcalls", func)
*
* we have this
*
* 0f 1f 05 b1 19 00 00 nopl func(%rip)
*
* we're going to turn it into this
*
* 67 67 e8 b1 19 00 00 addr32 addr32 call func
*/
__morph_begin();
for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) {
_base[*p + 0] = 0x67;
_base[*p + 1] = 0x67;
_base[*p + 2] = 0xe8;
}
__morph_end();
}
////////////////////////////////////////////////////////////////////////////////
// THE NEW TECHNOLOGY
@ -522,12 +495,11 @@ int sys_clone_linux(int flags, char *stk, int *ptid, int *ctid, void *tls,
* value from the thread by calling __get_tls(). There are a few
* layout expectations imposed by your C library. Those are all
* documented by __initialize_tls() which initializes the parts of
* the first 64 bytes of tls memory that libc cares about. Also
* note that if you decide to use tls once then you must use it
* for everything, since this flag also flips a runtime state that
* enables it for the main thread and functions such as
* __errno_location() will begin assuming they can safely access
* the tls segment register.
* the first 64 bytes of tls memory that libc cares about. This
* flag will transition the C runtime to the `__tls_enabled` state
* automatically. If it's used for one thread, then it must be
* used for all threads. The first time it's used, it must be used
* from the main thread.
* @param arg will be passed to your callback
* @param tls may be used to set the thread local storage segment;
* this parameter is ignored if `CLONE_SETTLS` is not set
@ -539,30 +511,15 @@ int sys_clone_linux(int flags, char *stk, int *ptid, int *ctid, void *tls,
*/
int clone(int (*func)(void *), void *stk, size_t stksz, int flags, void *arg,
int *ptid, void *tls, size_t tlssz, int *ctid) {
int rc, maintid;
int rc;
struct CloneArgs *wt;
// transition program to threaded state
if (!__threaded && (flags & CLONE_THREAD)) {
FixupThreadCalls();
}
if ((flags & CLONE_SETTLS) && !__tls_enabled) {
if (~flags & CLONE_THREAD) {
STRACE("clone() tls w/o thread");
return einval();
}
if (__threaded) {
STRACE("clone() tls/non-tls mixed order");
return einval();
}
maintid = gettid();
__initialize_tls(tibdefault);
*(int *)((char *)tibdefault + 0x38) = maintid;
*(int *)((char *)tibdefault + 0x3c) = __errno;
__install_tls(tibdefault);
__threaded = maintid;
} else if (flags & CLONE_THREAD) {
__threaded = gettid();
__enable_tls();
}
if ((flags & CLONE_THREAD) && !__threaded) {
__enable_threads();
}
if (!func) {

View file

@ -26,6 +26,8 @@ extern unsigned char _tbss_end[];
extern unsigned char _tls_size[];
void _init(void) hidden;
void __enable_tls(void) hidden;
void __enable_threads(void) hidden;
void __restorewintty(void) hidden;
void *__cxa_finalize(void *) hidden;
void cosmo(int, char **, char **, long (*)[2]) hidden wontreturn;

View file

@ -26,6 +26,10 @@ COSMOPOLITAN_C_START_
#define kFixedmapStart _kMem(0x300000000000, 0x000040000000)
#define kFixedmapSize \
_kMem(0x400000000000 - 0x300000000000, 0x000070000000 - 0x000040000000)
#define kMemtrackFdsStart _kMem(0x6fe000000000, 0x80000000)
#define kMemtrackFdsSize _kMem(0x001000000000, 0x04000000)
#define kMemtrackZiposStart _kMem(0x6fd000000000, 0x84000000)
#define kMemtrackZiposSize _kMem(0x001000000000, 0x20000000)
#define _kMmi(VSPACE) \
ROUNDUP(VSPACE / FRAMESIZE * (intptr_t)sizeof(struct MemoryInterval), \
FRAMESIZE)

View file

@ -0,0 +1,83 @@
# -*- conf -*-
# Cosmopolitan Libc Legacy Memory Plan
00000000-0000001f 2048kb guard
00000020-0000003f 2048kb loader
00000040-000000ff 12mb image
00000100-000003ff 48mb free
00000400-000007ff 64mb free
00000800-00000bff 64mb free
00000c00-00000fff 64mb free
00001000-000013ff 64mb automap
00001400-000017ff 64mb automap
00001800-00001bff 64mb automap
00001c00-00001fff 64mb automap
00002000-000023ff 64mb automap
00002400-000027ff 64mb automap
00002800-00002bff 64mb automap
00002c00-00002fff 64mb automap
00003000-000033ff 64mb automap
00003400-000037ff 64mb automap
00003800-00003bff 64mb automap
00003c00-00003fe7 63mb automap
00003fe4-00003ffb 1536kb memtrack
00003ffc-00003fff 256kb free
00004000-000043ff 64mb fixedmap
00004400-000047ff 64mb fixedmap
00004800-00004bff 64mb fixedmap
00004c00-00004fff 64mb fixedmap
00005000-000053ff 64mb fixedmap
00005400-000057ff 64mb fixedmap
00005800-00005bff 64mb fixedmap
00005c00-00005fff 64mb fixedmap
00006000-000063ff 64mb fixedmap
00006400-000067ff 64mb fixedmap
00006800-00006bff 64mb fixedmap
00006c00-00006fff 64mb fixedmap
00005000-000053ff 64mb arena
00005400-000057ff 64mb arena
00005800-00005bff 64mb arena
00005c00-00005fff 64mb arena
00006000-000063ff 64mb arena
00006400-000067ff 64mb arena
00006800-00006bff 64mb arena
00006c00-00006fff 64mb arena
00007000-000073ff 64mb arena
00007400-000077ff 64mb arena
00007800-00007bff 64mb arena
00007c00-00007ffd 64mb arena
00007ffe-00007fff 128kb free
00008000-000083ff 64mb fds
00008400-000087ff 64mb zipos
00008800-00008bff 64mb zipos
00008c00-00008fff 64mb zipos
00009000-000093ff 64mb zipos
00009400-000097ff 64mb zipos
00009800-00009bff 64mb zipos
00009c00-00009fff 64mb zipos
0000a000-0000a3ff 64mb zipos
0000a400-0000a7ff 64mb free
0000a800-0000abff 64mb free
0000ac00-0000afff 64mb free
0000b000-0000b3ff 64mb free
0000b400-0000b7ff 64mb free
0000b800-0000bbff 64mb free
0000bc00-0000bfff 64mb free
0000c000-0000c3ff 64mb free
0000c400-0000c7ff 64mb free
0000c800-0000cbff 64mb free
0000cc00-0000cfff 64mb free
0000d000-0000d3ff 64mb free
0000d400-0000d7ff 64mb free
0000d800-0000dbff 64mb free
0000dc00-0000dfff 64mb free
0000e000-0000e3ff 64mb free
0000e400-0000e7ff 64mb free
0000e800-0000ebff 64mb free
0000ec00-0000efff 64mb free
0000f000-0000f3ff 64mb free
0000f400-0000f7ff 64mb free
0000f800-0000fbff 64mb free
0000fc00-0000fffb 64mb free
0000fffc-0000fffd 128kb winargs
0000fffe-0000ffff 128kb stack

2073
libc/runtime/memtrack64.txt Normal file

File diff suppressed because it is too large Load diff

View file

@ -100,7 +100,7 @@ extern char ape_stack_align[] __attribute__((__weak__));
: "=r"(vAddr) \
: "i"(ADDEND)); \
} else { \
vAddr = 0x10000000; \
vAddr = 0x100000000 - GetStackSize(); \
} \
(void *)vAddr; \
})

56
libc/runtime/threadmode.c Normal file
View file

@ -0,0 +1,56 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/calls/calls.h"
#include "libc/errno.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
static char tibdefault[64];
extern int __threadcalls_end[];
extern int __threadcalls_start[];
void __enable_tls(void) {
__initialize_tls(tibdefault);
*(int *)((char *)tibdefault + 0x38) = gettid();
*(int *)((char *)tibdefault + 0x3c) = __errno;
__install_tls(tibdefault);
}
privileged void __enable_threads(void) {
__threaded = gettid();
/*
* _NOPL("__threadcalls", func)
*
* we have this
*
* 0f 1f 05 b1 19 00 00 nopl func(%rip)
*
* we're going to turn it into this
*
* 67 67 e8 b1 19 00 00 addr32 addr32 call func
*/
__morph_begin();
for (int *p = __threadcalls_start; p < __threadcalls_end; ++p) {
_base[*p + 0] = 0x67;
_base[*p + 1] = 0x67;
_base[*p + 2] = 0xe8;
}
__morph_end();
}

View file

@ -0,0 +1,16 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_WINARGS_INTERNAL_H_
#define COSMOPOLITAN_LIBC_RUNTIME_WINARGS_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct WinArgs {
char *argv[4096];
char *envp[4092];
intptr_t auxv[2][2];
char argblock[ARG_MAX / 2];
char envblock[ARG_MAX / 2];
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_WINARGS_INTERNAL_H_ */

View file

@ -55,6 +55,7 @@
#include "libc/runtime/internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/winargs.internal.h"
#include "libc/sock/internal.h"
#include "libc/str/str.h"
#include "libc/str/tpenc.h"
@ -81,14 +82,6 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
* TODO: How can we ensure we never overlap with KERNEL32.DLL?
*/
struct WinArgs {
char *argv[4096];
char *envp[4092];
intptr_t auxv[2][2];
char argblock[ARG_MAX / 2];
char envblock[ARG_MAX / 2];
};
extern uint32_t __winmainpid;
extern int64_t __wincrashearly;
extern const char kConsoleHandles[3];

View file

@ -40,7 +40,6 @@ hidden struct NtWsaData kNtWsaData;
static textwindows void WinSockCleanup(void) {
int i, rc;
NTTRACE("WinSockCleanup()");
rc = WSACleanup();
NTTRACE("WSACleanup() → %d% lm", rc);
}

View file

@ -16,19 +16,20 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/mem/mem.h"
#include "libc/nt/runtime.h"
#include "libc/zip.h"
#include "libc/dce.h"
#include "libc/zipos/zipos.internal.h"
/**
* Closes compressed object.
*
* @param fd is vetted by close()
* @asyncsignalsafe
* @threadsafe
* @vforksafe
*/
int __zipos_close(int fd) {
int rc;
@ -40,8 +41,7 @@ int __zipos_close(int fd) {
rc = 0; /* no system file descriptor needed on nt */
}
if (!__vforked) {
free(h->freeme);
free(h);
__zipos_free(__zipos_get(), h);
}
return rc;
}

40
libc/zipos/free.c Normal file
View file

@ -0,0 +1,40 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/str/str.h"
#include "libc/zipos/zipos.internal.h"
/**
* Frees ZipOS handle.
* @asyncsignalsafe
* @threadsafe
*/
void __zipos_free(struct Zipos *z, struct ZiposHandle *h) {
if (IsAsan()) {
__asan_poison((char *)h + sizeof(struct ZiposHandle),
h->mapsize - sizeof(struct ZiposHandle), kAsanHeapFree);
}
__zipos_lock();
do h->next = z->freelist;
while (!_cmpxchg(&z->freelist, h->next, h));
__zipos_unlock();
}

View file

@ -16,27 +16,15 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/bits/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pthread.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/mem/alloca.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sig.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.internal.h"
@ -63,52 +51,52 @@ static void __zipos_munmap_unneeded(const uint8_t *base, const uint8_t *cdir,
/**
* Returns pointer to zip central directory of current executable.
* @asyncsignalsafe (TODO: verify this)
* @asyncsignalsafe
* @threadsafe
*/
struct Zipos *__zipos_get(void) {
int fd;
char *path;
ssize_t size;
const char *msg;
static bool once;
sigset_t neu, old;
struct Zipos *res;
const char *progpath;
static struct Zipos zipos;
uint8_t *map, *base, *cdir;
static pthread_mutex_t lock;
pthread_mutex_lock(&lock);
if (_cmpxchg(&once, false, true)) {
sigfillset(&neu);
if (!once) {
__zipos_lock();
progpath = GetProgramExecutableName();
if ((fd = open(progpath, O_RDONLY)) != -1) {
if ((size = getfiledescriptorsize(fd)) != SIZE_MAX &&
if ((size = getfiledescriptorsize(fd)) != -1ul &&
(map = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED) {
if ((base = FindEmbeddedApe(map, size))) {
size -= base - map;
} else {
base = map;
}
if ((cdir = GetZipCdir(base, size))) {
if ((cdir = GetZipCdir(base, size)) && _cmpxchg(&zipos.map, 0, base)) {
__zipos_munmap_unneeded(base, cdir, map);
zipos.map = base;
zipos.cdir = cdir;
STRACE("__zipos_get(%#s)", progpath);
msg = "ok";
} else {
munmap(map, size);
STRACE("__zipos_get(%#s) → eocd not found", progpath);
msg = "eocd not found";
}
} else {
msg = "map failed";
}
close(fd);
} else {
STRACE("__zipos_get(%#s) → open failed %m", progpath);
msg = "open failed";
}
once = true;
__zipos_unlock();
STRACE("__zipos_get(%#s) → %s% m", progpath, msg);
}
if (zipos.cdir) {
res = &zipos;
} else {
res = 0;
}
pthread_mutex_unlock(&lock);
return res;
}

30
libc/zipos/lock.c Normal file
View file

@ -0,0 +1,30 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/intrin/pthread.h"
#include "libc/zipos/zipos.internal.h"
static pthread_mutex_t __zipos_lock_obj;
void(__zipos_lock)(void) {
pthread_mutex_lock(&__zipos_lock_obj);
}
void(__zipos_unlock)(void) {
pthread_mutex_unlock(&__zipos_lock_obj);
}

View file

@ -16,24 +16,26 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "ape/relocations.h"
#include "libc/assert.h"
#include "libc/bits/weaken.h"
#include "libc/bits/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/cmpxchg.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/lockcmpxchg.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/crc32.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/sysv/consts/f.h"
#include "libc/sysv/consts/fd.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
@ -41,64 +43,164 @@
#include "libc/sysv/errfuns.h"
#include "libc/zip.h"
#include "libc/zipos/zipos.internal.h"
#include "third_party/zlib/zlib.h"
static int __zipos_inflate(struct ZiposHandle *h, uint8_t *data, size_t size) {
return !__inflate(h->freeme, h->size, data, size) ? 0 : eio();
static volatile size_t maptotal;
static pureconst size_t __zipos_granularity(void) {
return (IsWindows() ? FRAMESIZE : PAGESIZE) * (IsAsan() ? 8 : 1);
}
static void *__zipos_mmap(size_t mapsize) {
size_t offset;
int prot, flags;
struct DirectMap dm;
uint64_t addr, addr2;
do offset = maptotal;
while (!_cmpxchg(&maptotal, offset, maptotal + mapsize));
if (offset + mapsize <= kMemtrackZiposSize) {
prot = PROT_READ | PROT_WRITE;
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
addr = kMemtrackZiposStart + offset;
if ((dm = sys_mmap((void *)addr, mapsize, prot, flags, -1, 0)).addr !=
MAP_FAILED) {
TrackMemoryInterval(&_mmi, addr >> 16, (addr + mapsize - 1) >> 16,
dm.maphandle, prot, flags, false, false, 0, mapsize);
if (IsAsan()) {
addr2 = (addr >> 3) + 0x7fff8000;
dm = sys_mmap((void *)addr2, mapsize >> 3, prot, flags, -1, 0);
TrackMemoryInterval(&_mmi, addr2 >> 16,
(addr2 + (mapsize >> 3) - 1) >> 16, dm.maphandle,
prot, flags, false, false, 0, mapsize >> 3);
}
return (void *)addr;
}
}
enomem();
return 0;
}
static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) {
size_t mapsize;
struct ZiposHandle *h, **ph;
__zipos_lock();
mapsize = sizeof(struct ZiposHandle) + size;
mapsize = ROUNDUP(mapsize, __zipos_granularity());
StartOver:
ph = &zipos->freelist;
while ((h = *ph)) {
if (h->mapsize >= mapsize) {
if (!_cmpxchg(ph, h, h->next)) goto StartOver;
h->next = 0;
break;
}
ph = &h->next;
}
if (!h) {
h = __zipos_mmap(mapsize);
}
__zipos_unlock();
if (IsAsan()) {
__asan_unpoison((char *)h, sizeof(struct ZiposHandle) + size);
__asan_poison((char *)h + sizeof(struct ZiposHandle) + size,
mapsize - (sizeof(struct ZiposHandle) + size),
kAsanHeapOverrun);
}
if (h) {
h->size = size;
h->mapsize = mapsize;
}
return h;
}
static int __zipos_mkfd(int minfd) {
int e, fd;
static bool demodernize;
if (!demodernize) {
e = errno;
if ((fd = __sys_fcntl(2, F_DUPFD_CLOEXEC, minfd)) != -1) {
return fd;
} else if (errno == EINVAL) {
demodernize = true;
errno = e;
} else {
return fd;
}
}
if ((fd = __sys_fcntl(2, F_DUPFD, minfd)) != -1) {
__sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
}
return fd;
}
static int __zipos_setfd(int fd, struct ZiposHandle *h, unsigned flags,
int mode) {
_cmpxchg(&g_fds.f, fd, fd + 1);
g_fds.p[fd].kind = kFdZip;
g_fds.p[fd].handle = (intptr_t)h;
g_fds.p[fd].flags = flags | O_CLOEXEC;
g_fds.p[fd].mode = mode;
g_fds.p[fd].extra = 0;
__fds_unlock();
return fd;
}
static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
int mode) {
size_t lf;
int rc, fd;
size_t size;
uint8_t *data;
int rc, fd, minfd;
struct ZiposHandle *h;
lf = GetZipCfileOffset(zipos->map + cf);
if (!IsTiny() &&
(ZIP_LFILE_MAGIC(zipos->map + lf) != kZipLfileHdrMagic ||
(ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) != kZipCompressionNone &&
ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf) !=
kZipCompressionDeflate))) {
return eio();
}
if (!(h = calloc(1, sizeof(*h)))) return -1;
h->cfile = cf;
h->size = GetZipLfileUncompressedSize(zipos->map + lf);
if (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
if ((h->freeme = malloc(h->size))) {
data = ZIP_LFILE_CONTENT(zipos->map + lf);
size = GetZipLfileCompressedSize(zipos->map + lf);
if ((rc = __zipos_inflate(h, data, size)) != -1) {
h->mem = h->freeme;
assert((ZIP_LFILE_MAGIC(zipos->map + lf) == kZipLfileHdrMagic));
size = GetZipLfileUncompressedSize(zipos->map + lf);
switch (ZIP_LFILE_COMPRESSIONMETHOD(zipos->map + lf)) {
case kZipCompressionNone:
if (!(h = __zipos_alloc(zipos, 0))) return -1;
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
break;
case kZipCompressionDeflate:
if (!(h = __zipos_alloc(zipos, size))) return -1;
if (!__inflate(h->data, size, ZIP_LFILE_CONTENT(zipos->map + lf),
GetZipLfileCompressedSize(zipos->map + lf))) {
h->mem = h->data;
} else {
h->mem = 0;
eio();
}
}
} else {
h->mem = ZIP_LFILE_CONTENT(zipos->map + lf);
break;
default:
return eio();
}
h->pos = 0;
h->cfile = cf;
h->size = size;
if (!IsTiny() && h->mem &&
crc32_z(0, h->mem, h->size) != ZIP_LFILE_CRC32(zipos->map + lf)) {
errno = EIO;
h->mem = NULL;
h->mem = 0;
eio();
}
if (h->mem) {
if ((fd = IsWindows() ? __reservefd(-1) : dup(2)) != -1) {
if (__ensurefds(fd) != -1) {
__fds_lock();
h->handle = g_fds.p[fd].handle;
g_fds.p[fd].kind = kFdZip;
g_fds.p[fd].handle = (intptr_t)h;
g_fds.p[fd].flags = flags | O_CLOEXEC;
g_fds.p[fd].mode = mode;
g_fds.p[fd].extra = 0;
__fds_unlock();
return fd;
minfd = 3;
__fds_lock();
TryAgain:
if (IsWindows()) {
if ((fd = __reservefd_unlocked(-1)) != -1) {
return __zipos_setfd(fd, h, flags, mode);
}
close(fd);
} else if ((fd = __zipos_mkfd(minfd)) != -1) {
if (__ensurefds_unlocked(fd) != -1) {
if (g_fds.p[fd].kind) {
sys_close(fd);
minfd = fd + 1;
goto TryAgain;
}
return __zipos_setfd(fd, h, flags, mode);
}
sys_close(fd);
}
__fds_unlock();
}
free(h->freeme);
free(h);
__zipos_free(zipos, h);
return -1;
}
@ -106,12 +208,12 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags,
* Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store.
*
* @param uri is obtained via __zipos_parseuri()
* @note don't call open() from signal handlers
* @asyncsignalsafe (todo)
* @threadsafe
*/
int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) {
int rc;
ssize_t cf;
sigset_t oldmask;
struct Zipos *zipos;
if ((flags & O_ACCMODE) == O_RDONLY) {
if ((zipos = __zipos_get())) {

View file

@ -1,33 +1,40 @@
#ifndef COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
#define COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_
#include "libc/calls/calls.h"
#include "libc/intrin/nopl.h"
#include "libc/nexgen32e/threaded.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct stat;
struct iovec;
struct Zipos {
uint8_t *map;
uint8_t *cdir;
};
struct ZiposUri {
const char *path;
size_t len;
};
struct ZiposHandle {
uint8_t *mem; /* uncompressed file memory */
size_t size; /* byte length of file memory */
struct ZiposHandle *next;
size_t size; /* byte length of `mem` */
size_t mapsize; /* total size of this struct */
size_t pos; /* read/write byte offset state */
uint32_t cfile; /* central directory entry rva */
int64_t handle;
uint8_t *freeme;
uint8_t *mem; /* points to inflated data or uncompressed image */
uint8_t data[]; /* uncompressed file memory */
};
struct Zipos {
uint8_t *map;
uint8_t *cdir;
struct ZiposHandle *freelist;
};
void __zipos_lock(void) hidden;
void __zipos_unlock(void) hidden;
int __zipos_close(int) hidden;
struct Zipos *__zipos_get(void) pureconst hidden;
void __zipos_free(struct Zipos *, struct ZiposHandle *) hidden;
ssize_t __zipos_parseuri(const char *, struct ZiposUri *) hidden;
ssize_t __zipos_find(struct Zipos *, const struct ZiposUri *);
int __zipos_open(const struct ZiposUri *, unsigned, int) hidden;
@ -42,6 +49,14 @@ int64_t __zipos_lseek(struct ZiposHandle *, int64_t, unsigned) hidden;
int __zipos_fcntl(int, int, uintptr_t) hidden;
int __zipos_notat(int, const char *) hidden;
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__STRICT_ANSI__)
#define __zipos_lock() _NOPL0("__threadcalls", __zipos_lock)
#define __zipos_unlock() _NOPL0("__threadcalls", __zipos_unlock)
#else
#define __zipos_lock() (__threaded ? __zipos_lock() : 0)
#define __zipos_unlock() (__threaded ? __zipos_unlock() : 0)
#endif
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_ZIPOS_ZIPOS_H_ */

View file

@ -0,0 +1,135 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net 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/calls/calls.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/itimerval.h"
#include "libc/calls/struct/rlimit.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/spinlock.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/rand/rand.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/clone.h"
#include "libc/sysv/consts/itimer.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/rlimit.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
STATIC_YOINK("zip_uri_support");
STATIC_YOINK("libc/testlib/hyperion.txt");
#define THREADS 8
void PullSomeZipFilesIntoLinkage(void) {
gmtime(0);
}
char *stack[THREADS];
char tls[THREADS][64];
TEST(reservefd, testGrowthOfFdsDataStructure) {
int i, n;
struct rlimit rlim;
ASSERT_EQ(g_fds.n, OPEN_MAX);
n = 1700; // pe '2**16/40' → 1638 (likely value of g_fds.n)
if (!getrlimit(RLIMIT_NOFILE, &rlim)) n = MIN(n, rlim.rlim_cur - 3);
for (i = 0; i < n; ++i) {
EXPECT_SYS(0, i + 3, open("/zip/usr/share/zoneinfo/UTC", O_RDONLY));
}
ASSERT_GT(g_fds.n, OPEN_MAX);
for (i = 0; i < n; ++i) {
EXPECT_SYS(0, 0, close(i + 3));
}
}
void OnSigAlrm(int sig, siginfo_t *si, ucontext_t *ctx) {
int rc, fd;
char buf[64];
ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY)));
for (;;) {
rc = read(fd, buf, 64);
if (rc != -1) {
ASSERT_EQ(64, rc);
break;
} else {
ASSERT_EQ(EINTR, errno);
errno = 0;
}
}
ASSERT_EQ(0, memcmp(kHyperion, buf, 64));
close(fd); // can eintr which doesn't matter
}
int Worker(void *p) {
char buf[64];
int i, rc, fd;
for (i = 0; i < 64; ++i) {
ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY)));
usleep(rand64() % 100);
for (;;) {
rc = read(fd, buf, 64);
if (rc != -1) {
ASSERT_EQ(64, rc);
break;
} else {
ASSERT_EQ(EINTR, errno);
errno = 0;
}
}
ASSERT_EQ(0, memcmp(kHyperion, buf, 64));
close(fd);
}
return 0;
}
TEST(reservefd, tortureTest) {
int i;
struct sigaction oldsa;
struct itimerval oldit;
struct itimerval it = {{0, 10000}, {0, 100}};
struct sigaction sa = {.sa_sigaction = OnSigAlrm,
.sa_flags = 0 /* SA_NODEFER */};
// ASSERT_SYS(0, 0, sigaction(SIGALRM, &sa, &oldsa));
// ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &it, &oldit));
for (i = 0; i < THREADS; ++i) {
clone(Worker,
(stack[i] = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
MAP_STACK | MAP_ANONYMOUS, -1, 0)),
GetStackSize(),
CLONE_THREAD | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | CLONE_SETTLS,
0, 0, __initialize_tls(tls[i]), sizeof(tls[i]),
(int *)(tls[i] + 0x38));
}
for (i = 0; i < THREADS; ++i) {
_spinlock((int *)(tls[i] + 0x38));
}
// EXPECT_SYS(0, 0, sigaction(SIGALRM, &oldsa, 0));
// EXPECT_SYS(0, 0, setitimer(ITIMER_REAL, &oldit, 0));
}

View file

@ -45,6 +45,7 @@ TEST_LIBC_STR_DIRECTDEPS = \
LIBC_STR \
LIBC_STUBS \
LIBC_SYSV \
LIBC_SYSV_CALLS \
LIBC_TESTLIB \
LIBC_UNICODE \
LIBC_X \

View file

@ -169,10 +169,10 @@ static int klibc_fcntl(int fd, int action, /* arg */...);
FD_CLOEXEC is portable, but other flags may be present); otherwise
return -1 and set errno. */
int fcntl(int fd, int action, /* arg */...)
int fcntl_(int fd, int action, /* arg */...)
#undef fcntl
#ifdef __KLIBC__
#define fcntl klibc_fcntl
#define fcntl_ klibc_fcntl
#endif
{
va_list arg;

View file

@ -4,6 +4,7 @@
Python 3
https://docs.python.org/3/license.html │
*/
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "third_party/python/Include/yoink.h"
#include "third_party/python/runpythonmodule.h"

View file

@ -691,6 +691,7 @@
(define-key lua-mode-map (kbd "C-c C-r") 'cosmo-run)
(define-key python-mode-map (kbd "C-c C-r") 'cosmo-run)
(define-key c-mode-map (kbd "C-c C-s") 'cosmo-run-test)
(define-key c-mode-map (kbd "C-c C-_") 'cosmo-run-win7)
(define-key c-mode-map (kbd "C-c C-_") 'cosmo-run-win10))

View file

@ -6018,6 +6018,10 @@ static char *SetStatus(unsigned code, const char *reason) {
}
statuscode = code;
hascontenttype = istext = false; // reset, as the headers are reset
gotxcontenttypeoptions = 0;
gotcachecontrol = 0;
referrerpolicy = 0;
branded = 0;
stpcpy(hdrbuf.p, "HTTP/1.0 000 ");
hdrbuf.p[7] += msg.version & 1;
hdrbuf.p[9] += code / 100;

94
tool/viz/memplan.c Normal file
View file

@ -0,0 +1,94 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/bits/bits.h"
#include "libc/calls/internal.h"
#include "libc/fmt/itoa.h"
#include "libc/macros.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/stdio/stdio.h"
// clang-format off
struct WinArgs {
char *argv[4096];
char *envp[4092];
intptr_t auxv[2][2];
char argblock[ARG_MAX / 2];
char envblock[ARG_MAX / 2];
};
//#define INCREMENT roundup2pow(kAutomapSize/16)
#define INCREMENT (64L*1024*1024*1024)
uint64_t last;
void plan2(uint64_t addr, uint64_t end, const char *name) {
char sz[32];
FormatMemorySize(sz, end-addr+1);
printf("%08x-%08x %-6s %s\n", addr>>16, end>>16, sz, name);
}
void plan(uint64_t addr, uint64_t end, const char *name) {
uint64_t incr, next;
while (addr > last) {
if ((incr = last % INCREMENT)) {
incr = INCREMENT - incr;
} else {
incr = INCREMENT;
}
plan2(last,MIN(last+incr,addr)-1,"free");
last = MIN(last+incr,addr);
}
while (addr <= end) {
if (end-addr+1 <= INCREMENT) {
plan2(addr,end,name);
last = end + 1;
break;
}
if (!(addr % INCREMENT)) {
plan2(addr, addr + INCREMENT - 1, name);
last = addr + INCREMENT;
addr += INCREMENT;
} else {
next = INCREMENT - addr % INCREMENT + addr;
plan2(addr, next - 1, name);
last = next;
addr = next;
}
}
}
int main(int argc, char *argv[]) {
uint64_t x, y;
plan(0x00000000, 0x00200000-1, "null");
plan(0x00200000, 0x00400000-1, "loader");
if (!IsWindows() || IsAtLeastWindows10()) {
plan(0x00400000, 0x50000000-1, "image");
plan(0x50000000, 0x7ffdffff, "arena");
plan(0x7fff0000, 0x10007fffffff, "asan");
} else {
plan(0x00400000, 0x01000000-1, "image");
}
plan(kAutomapStart, kAutomapStart + kAutomapSize - 1, "automap");
plan(kMemtrackStart, kMemtrackStart + kMemtrackSize - 1, "memtrack");
plan(kFixedmapStart, kFixedmapStart + kFixedmapSize - 1, "fixedmap");
if (IsWindows() && !IsAtLeastWindows10()) {
plan(0x50000000, 0x7ffdffff, "arena");
}
x = (intptr_t)GetStaticStackAddr(0);
y = ROUNDUP(sizeof(struct WinArgs), FRAMESIZE);
plan(x - y, x - 1, "winargs");
plan(x, x + GetStackSize() - 1, "stack");
if (!IsWindows() || IsAtLeastWindows10()) {
plan(0x7f0000000000, 0x800000000000 - 1, "kernel");
}
return 0;
}