Don't relocate file descriptor memory

This change fixes #496 where ASAN spotted a race condition that could
happen in multithreaded programs, with more than OPEN_MAX descriptors
when using ZipOS or Windows NT, which require tracking open file info
and this change fixes that table so it never relocates, thus allowing
us to continue to enjoy the benefits of avoiding locks while reading.
This commit is contained in:
Justine Tunney 2022-09-09 16:54:28 -07:00
parent c3208eb9d5
commit 3265324e00
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
35 changed files with 297 additions and 152 deletions

View file

@ -211,6 +211,7 @@ include test/libc/fmt/test.mk
include test/libc/dns/test.mk
include test/libc/time/test.mk
include test/libc/stdio/test.mk
include test/libc/zipos/test.mk
include test/libc/release/test.mk
include test/libc/test.mk
include test/net/http/test.mk

View file

@ -7,16 +7,12 @@
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/errno.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/exit.h"
#include "libc/sysv/consts/s.h"
#include "libc/x/x.h"
#include "third_party/musl/ftw.h"
/**

View file

@ -79,14 +79,6 @@ o/$(MODE)/libc/calls/vdsofunc.greg.o: private \
-ffreestanding \
-fno-sanitize=address
# we can't use asan because:
# asan guard pages haven't been allocated yet
o/$(MODE)/libc/calls/directmap.o \
o/$(MODE)/libc/calls/directmap-nt.o: private \
OVERRIDE_COPTS += \
-ffreestanding \
-fno-sanitize=address
# we can't use asan because:
# ntspawn allocates 128kb of heap memory via win32
o/$(MODE)/libc/calls/ntspawn.o \
@ -144,12 +136,6 @@ o/$(MODE)/libc/calls/ioctl-siocgifconf-nt.o: private \
-ffunction-sections \
-fdata-sections
# we want small code size because:
# to keep .text.head under 4096 bytes
o/$(MODE)/libc/calls/mman.greg.o: private \
OVERRIDE_COPTS += \
-Os
# we always want -Os because:
# va_arg codegen is very bloated in default mode
o//libc/calls/open.o \

View file

@ -0,0 +1,10 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_EXTEND_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_EXTEND_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void *_extend(void *, size_t, void *, intptr_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_EXTEND_INTERNAL_H_ */

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/extend.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/strace.internal.h"
@ -46,33 +47,11 @@ static volatile size_t mapsize;
* @asyncsignalsafe
*/
int __ensurefds_unlocked(int fd) {
uint64_t addr;
int prot, flags;
size_t size, chunk;
struct DirectMap dm;
bool relocate;
if (fd < g_fds.n) return fd;
STRACE("__ensurefds(%d) extending", fd);
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);
}
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;
g_fds.n = fd + 1;
g_fds.e =
_extend(g_fds.p, g_fds.n * sizeof(*g_fds.p), g_fds.e, 0x6ff000000000);
return fd;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/sig.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/strace.internal.h"

View file

@ -26,9 +26,8 @@ struct Fd {
struct Fds {
int f; /* lowest free slot */
size_t n; /* monotonic capacity */
struct Fd *p;
struct Fd __init_p[OPEN_MAX];
size_t n;
struct Fd *p, *e;
};
COSMOPOLITAN_C_END_

View file

@ -67,8 +67,8 @@
#endif
/* TODO(jart): Remove this in favor of GetStackSize() */
#if defined(COSMO) && defined(MODE_DBG)
#define STACKSIZE 131072 /* 128kb stack */
#if defined(COSMO) && (defined(MODE_DBG) || defined(__SANITIZE_ADDRESS__))
#define STACKSIZE 262144 /* 256kb stack */
#elif defined(COSMO)
#define STACKSIZE 65536 /* 64kb stack */
#else

84
libc/intrin/extend.c Normal file
View file

@ -0,0 +1,84 @@
/*-*- 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/assert.h"
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/asancodes.h"
#include "libc/macros.internal.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#define G FRAMESIZE
static void _mapframe(void *p) {
int prot, flags;
struct DirectMap dm;
prot = PROT_READ | PROT_WRITE;
flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
if ((dm = sys_mmap(p, G, prot, flags, -1, 0)).addr != p) {
notpossible;
}
if (TrackMemoryInterval(&_mmi, (uintptr_t)p >> 16,
((uintptr_t)p + G - 1) >> 16, dm.maphandle, prot,
flags, false, false, 0, G)) {
notpossible;
}
}
/**
* Extends static allocation.
*
* This simple fixed allocator has unusual invariants
*
* !(p & 0xffff) && !(((p >> 3) + 0x7fff8000) & 0xffff)
*
* which must be the case when selecting a starting address.
*
* @param p points to start of memory region
* @param n specifies how many bytes are needed
* @param e points to end of memory that's allocated
* @param h is highest address to which `e` may grow
* @return new value for `e`
*/
noasan void *_extend(void *p, size_t n, void *e, intptr_t h) {
char *q;
#ifndef NDEBUG
if ((uintptr_t)SHADOW(p) & (G - 1)) notpossible;
if ((uintptr_t)p + (G << kAsanScale) > h) notpossible;
#endif
for (q = e; q < ((char *)p + n); q += 8) {
if (!((uintptr_t)q & (G - 1))) {
if (q + G > (char *)h) notpossible;
_mapframe(q);
if (IsAsan()) {
if (!((uintptr_t)SHADOW(q) & (G - 1))) {
_mapframe(SHADOW(q));
__asan_poison(q, G << kAsanScale, kAsanProtected);
}
}
}
if (IsAsan()) {
*SHADOW(q) = 0;
}
}
return q;
}

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/extend.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/intrin/pthread.h"
@ -33,8 +34,8 @@ static textwindows dontinline void SetupWinStd(struct Fds *fds, int i, int x) {
int64_t h;
h = GetStdHandle(x);
if (!h || h == -1) return;
fds->__init_p[i].kind = pushpop(kFdFile);
fds->__init_p[i].handle = h;
fds->p[i].kind = pushpop(kFdFile);
fds->p[i].handle = h;
fds->f = i + 1;
}
@ -42,29 +43,30 @@ textstartup void InitializeFileDescriptors(void) {
struct Fds *fds;
__fds_lock_obj.type = PTHREAD_MUTEX_RECURSIVE;
fds = VEIL("r", &g_fds);
pushmov(&fds->n, ARRAYLEN(fds->__init_p));
fds->p = fds->e = (void *)0x6fe000040000;
fds->n = 4;
fds->f = 3;
fds->p = fds->__init_p;
fds->e = _extend(fds->p, fds->n * sizeof(*fds->p), fds->e, 0x6ff000000000);
if (IsMetal()) {
extern const char vga_console[];
pushmov(&fds->f, 3ull);
if (weaken(vga_console)) {
fds->__init_p[0].kind = pushpop(kFdConsole);
fds->__init_p[1].kind = pushpop(kFdConsole);
fds->__init_p[2].kind = pushpop(kFdConsole);
fds->p[0].kind = pushpop(kFdConsole);
fds->p[1].kind = pushpop(kFdConsole);
fds->p[2].kind = pushpop(kFdConsole);
} else {
fds->__init_p[0].kind = pushpop(kFdSerial);
fds->__init_p[1].kind = pushpop(kFdSerial);
fds->__init_p[2].kind = pushpop(kFdSerial);
fds->p[0].kind = pushpop(kFdSerial);
fds->p[1].kind = pushpop(kFdSerial);
fds->p[2].kind = pushpop(kFdSerial);
}
fds->__init_p[0].handle = VEIL("r", 0x3F8ull);
fds->__init_p[1].handle = VEIL("r", 0x3F8ull);
fds->__init_p[2].handle = VEIL("r", 0x3F8ull);
fds->p[0].handle = VEIL("r", 0x3F8ull);
fds->p[1].handle = VEIL("r", 0x3F8ull);
fds->p[2].handle = VEIL("r", 0x3F8ull);
} else if (IsWindows()) {
SetupWinStd(fds, 0, kNtStdInputHandle);
SetupWinStd(fds, 1, kNtStdOutputHandle);
SetupWinStd(fds, 2, kNtStdErrorHandle);
}
fds->__init_p[1].flags = O_WRONLY | O_APPEND;
fds->__init_p[2].flags = O_WRONLY | O_APPEND;
fds->p[1].flags = O_WRONLY | O_APPEND;
fds->p[2].flags = O_WRONLY | O_APPEND;
}

View file

@ -43,6 +43,20 @@ $(LIBC_INTRIN_A).pkg: \
$(LIBC_INTRIN_A_OBJS) \
$(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x)_A).pkg)
# we can't use asan because:
# asan guard pages haven't been allocated yet
o/$(MODE)/libc/intrin/directmap.o \
o/$(MODE)/libc/intrin/directmap-nt.o: private \
OVERRIDE_COPTS += \
-ffreestanding \
-fno-sanitize=address
# we want small code size because:
# to keep .text.head under 4096 bytes
o/$(MODE)/libc/intrin/mman.greg.o: private \
OVERRIDE_COPTS += \
-Os
# we can't use asan and ubsan because:
# this is asan and ubsan
o/$(MODE)/libc/intrin/asan.o \

View file

@ -1,9 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_LOG_LIBFATAL_INTERNAL_H_
#define COSMOPOLITAN_LIBC_LOG_LIBFATAL_INTERNAL_H_
#include "libc/calls/calls.h"
#include "libc/dce.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/nt/runtime.h"
#include "libc/sysv/consts/nr.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
@ -206,7 +204,7 @@ forceinline char *__fixcpy(char p[hasatleast 17], uint64_t x, uint8_t k) {
}
forceinline char *__hexcpy(char p[hasatleast 17], uint64_t x) {
return __fixcpy(p, x, ROUNDUP(x ? bsrl(x) + 1 : 1, 4));
return __fixcpy(p, x, ROUNDUP(x ? (__builtin_clzll(x) ^ 63) + 1 : 1, 4));
}
forceinline const void *__memchr(const void *s, unsigned char c, size_t n) {

View file

@ -121,8 +121,7 @@ void ShowCrashReports(void) {
ss.ss_size = GetStackSize();
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
if ((sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) {
if ((sp = _mapanon(GetStackSize()))) {
ss.ss_sp = sp;
if (!sigaltstack(&ss, &g_oldsigaltstack)) {
__cxa_atexit(FreeSigAltStack, ss.ss_sp, 0);

View file

@ -18,7 +18,6 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"

View file

@ -226,9 +226,9 @@ textwindows void WinMainForked(void) {
// rewrap the stdin named pipe hack
// since the handles closed on fork
struct Fds *fds = VEIL("r", &g_fds);
fds->p[0].handle = fds->__init_p[0].handle = GetStdHandle(kNtStdInputHandle);
fds->p[1].handle = fds->__init_p[1].handle = GetStdHandle(kNtStdOutputHandle);
fds->p[2].handle = fds->__init_p[2].handle = GetStdHandle(kNtStdErrorHandle);
fds->p[0].handle = GetStdHandle(kNtStdInputHandle);
fds->p[1].handle = GetStdHandle(kNtStdOutputHandle);
fds->p[2].handle = GetStdHandle(kNtStdErrorHandle);
// untrack children of parent since we specify with both
// CreateProcess() and CreateThread() as non-inheritable

View file

@ -16,16 +16,9 @@
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/sigset.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/bits.h"
#include "libc/log/libfatal.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/prot.h"
/**
* Rewrites code in memory to hook function calls.

View file

@ -1810,7 +1810,7 @@
6fb00000-6fbfffff 64gb free
6fc00000-6fcfffff 64gb free
6fd00000-6fdfffff 64gb zipos
6fe00000-6fefffff 64gb g_fds
6fe00004-6feffffc 64gb g_fds
6ff00000-6ffffffd 64gb free
6ffffffe-6fffffff 128kb winargs

View file

@ -24,6 +24,7 @@
*/
#include "libc/fmt/fmt.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "third_party/gdtoa/gdtoa.h"
@ -33,10 +34,24 @@ Copyright (c) 2002, 2006, 2010 Todd C. Miller <millert@openbsd.org>\"");
asm(".include \"libc/disclaimer.inc\"");
// clang-format off
static char *s;
static void
__cvt_atexit(void)
{
free(s);
s = 0;
}
static void __attribute__((__constructor__))
__cvt_init(void)
{
atexit(__cvt_atexit);
}
static char *
__cvt(double value, int ndigit, int *decpt, int *sign, int fmode, int pad)
{
static char *s;
char *p, *rve, c;
size_t siz;

View file

@ -23,11 +23,6 @@
#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),

View file

@ -18,6 +18,7 @@
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/extend.internal.h"
#include "libc/calls/internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/struct/sigset.h"
@ -42,47 +43,19 @@
#include "libc/zip.h"
#include "libc/zipos/zipos.internal.h"
static volatile size_t maptotal;
static pureconst size_t __zipos_granularity(void) {
return FRAMESIZE * (IsAsan() ? 8 : 1);
}
static char *mapend;
static size_t maptotal;
static void *__zipos_mmap(size_t mapsize) {
char *start;
size_t offset;
struct DirectMap dm;
int rc, prot, flags;
uint64_t addr, addr2, addr3;
assert(mapsize);
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) {
rc = TrackMemoryInterval(&_mmi, addr >> 16, (addr + mapsize - 1) >> 16,
dm.maphandle, prot, flags, false, false, 0,
mapsize);
assert(!rc);
if (IsAsan()) {
addr2 = (addr >> 3) + 0x7fff8000;
addr3 = ((addr + mapsize) >> 3) + 0x7fff8000;
assert(!(addr2 & (FRAMESIZE - 1)));
assert(!(addr3 & (FRAMESIZE - 1)));
dm = sys_mmap((void *)addr2, mapsize >> 3, prot, flags, -1, 0);
assert(dm.addr != MAP_FAILED);
rc = TrackMemoryInterval(&_mmi, addr2 >> 16, (addr3 >> 16) - 1,
dm.maphandle, prot, flags, false, false, 0,
mapsize >> 3);
assert(!rc);
}
return (void *)addr;
}
}
enomem();
return 0;
offset = maptotal;
maptotal += mapsize;
start = (char *)0x6fd000040000;
if (!mapend) mapend = start;
mapend = _extend(start, maptotal, mapend, 0x6fdfffff0000);
return start + offset;
}
static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) {
@ -90,7 +63,7 @@ static struct ZiposHandle *__zipos_alloc(struct Zipos *zipos, size_t size) {
struct ZiposHandle *h, **ph;
__zipos_lock();
mapsize = sizeof(struct ZiposHandle) + size;
mapsize = ROUNDUP(mapsize, __zipos_granularity());
mapsize = ROUNDUP(mapsize, 4096);
StartOver:
ph = &zipos->freelist;
while ((h = *ph)) {

0
libc/zipos/zipos.h Executable file
View file

View file

@ -1,6 +1,5 @@
#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)

View file

@ -61,7 +61,6 @@ void PullSomeZipFilesIntoLinkage(void) {
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) {

View file

@ -19,4 +19,5 @@ o/$(MODE)/test/libc: \
o/$(MODE)/test/libc/time \
o/$(MODE)/test/libc/tinymath \
o/$(MODE)/test/libc/x \
o/$(MODE)/test/libc/zipos \
o/$(MODE)/test/libc/xed

View file

@ -26,6 +26,7 @@
#include "libc/intrin/pthread2.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/nexgen32e.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/sysv/consts/prot.h"
@ -37,18 +38,19 @@
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
void OnTrap(int sig, struct siginfo *si, void *vctx) {
void OnUsr1(int sig, struct siginfo *si, void *vctx) {
struct ucontext *ctx = vctx;
}
void SetUp(void) {
struct sigaction sig = {.sa_sigaction = OnTrap, .sa_flags = SA_SIGINFO};
sigaction(SIGTRAP, &sig, 0);
struct sigaction sig = {.sa_sigaction = OnUsr1, .sa_flags = SA_SIGINFO};
sigaction(SIGUSR1, &sig, 0);
}
void TriggerSignal(void) {
sched_yield();
DebugBreak();
/* kprintf("raising at %p\n", __builtin_frame_address(0)); */
raise(SIGUSR1);
sched_yield();
}
@ -67,6 +69,7 @@ TEST(pthread_create, testCreateReturnJoin) {
}
static void *IncExit(void *arg) {
CheckStackIsAligned();
TriggerSignal();
pthread_exit((void *)((uintptr_t)arg + 1));
}

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/mem/mem.h"
#include "libc/runtime/gc.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/hyperion.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/spawn.h"
#include "libc/zipos/zipos.h"
STATIC_YOINK("zip_uri_support");
STATIC_YOINK("libc/testlib/hyperion.txt");
/* STATIC_YOINK("inflate"); */
/* STATIC_YOINK("inflateInit2"); */
/* STATIC_YOINK("inflateEnd"); */
int Worker(void *arg, int tid) {
int i, fd;
char *data;
for (i = 0; i < 20; ++i) {
ASSERT_NE(-1, (fd = open("/zip/libc/testlib/hyperion.txt", O_RDONLY)));
data = malloc(kHyperionSize);
ASSERT_EQ(kHyperionSize, read(fd, data, kHyperionSize));
ASSERT_EQ(0, memcmp(data, kHyperion, kHyperionSize));
ASSERT_SYS(0, 0, close(fd));
free(data);
}
return 0;
}
TEST(zipos, test) {
int i, n = 16;
struct spawn *t = _gc(malloc(sizeof(struct spawn) * n));
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, _spawn(Worker, 0, t + i));
for (i = 0; i < n; ++i) EXPECT_SYS(0, 0, _join(t + i));
/* __print_maps(); */
}

63
test/libc/zipos/test.mk Normal file
View file

@ -0,0 +1,63 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += TEST_LIBC_ZIPOS
TEST_LIBC_ZIPOS_SRCS := $(wildcard test/libc/zipos/*.c)
TEST_LIBC_ZIPOS_SRCS_TEST = $(filter %_test.c,$(TEST_LIBC_ZIPOS_SRCS))
TEST_LIBC_ZIPOS_OBJS = \
$(TEST_LIBC_ZIPOS_SRCS:%.c=o/$(MODE)/%.o)
TEST_LIBC_ZIPOS_COMS = \
$(TEST_LIBC_ZIPOS_SRCS:%.c=o/$(MODE)/%.com)
TEST_LIBC_ZIPOS_BINS = \
$(TEST_LIBC_ZIPOS_COMS) \
$(TEST_LIBC_ZIPOS_COMS:%=%.dbg)
TEST_LIBC_ZIPOS_TESTS = \
$(TEST_LIBC_ZIPOS_SRCS_TEST:%.c=o/$(MODE)/%.com.ok)
TEST_LIBC_ZIPOS_CHECKS = \
$(TEST_LIBC_ZIPOS_SRCS_TEST:%.c=o/$(MODE)/%.com.runs)
TEST_LIBC_ZIPOS_DIRECTDEPS = \
LIBC_CALLS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_MEM \
LIBC_NEXGEN32E \
LIBC_RUNTIME \
LIBC_STR \
LIBC_STUBS \
LIBC_THREAD \
LIBC_SYSV \
LIBC_ZIPOS \
LIBC_TIME \
LIBC_TESTLIB \
THIRD_PARTY_ZLIB
TEST_LIBC_ZIPOS_DEPS := \
$(call uniq,$(foreach x,$(TEST_LIBC_ZIPOS_DIRECTDEPS),$($(x))))
o/$(MODE)/test/libc/zipos/zipos.pkg: \
$(TEST_LIBC_ZIPOS_OBJS) \
$(foreach x,$(TEST_LIBC_ZIPOS_DIRECTDEPS),$($(x)_A).pkg)
#o/$(MODE)/libc/testlib/hyperion.txt.zip.o: private ZIPOBJ_FLAGS += -0
o/$(MODE)/test/libc/zipos/%.com.dbg: \
$(TEST_LIBC_ZIPOS_DEPS) \
o/$(MODE)/test/libc/zipos/%.o \
o/$(MODE)/test/libc/zipos/zipos.pkg \
o/$(MODE)/libc/testlib/hyperion.txt.zip.o \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
.PHONY: o/$(MODE)/test/libc/zipos
o/$(MODE)/test/libc/zipos: \
$(TEST_LIBC_ZIPOS_BINS) \
$(TEST_LIBC_ZIPOS_CHECKS)

View file

@ -1,20 +0,0 @@
#!/usr/bin/env python.com
import os
import sys
def CheckFile(path):
if path.endswith(('.png', '.ico')):
return
sys.stderr.write('%s\n' % (path))
with open(path) as f:
data = f.read()
assert '#include' not in data[65530:], "late include in %s" % (path)
for arg in sys.argv[1:]:
if os.path.isdir(arg):
for dirpath, dirs, files in os.walk(arg):
for filepath in files:
CheckFile(os.path.join(dirpath, filepath))
else:
CheckFile(arg)