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

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

@ -1,249 +0,0 @@
/*-*- 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 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/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/likely.h"
#include "libc/bits/weaken.h"
#include "libc/calls/calls.h"
#include "libc/calls/strace.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/intrin/asan.internal.h"
#include "libc/log/libfatal.internal.h"
#include "libc/log/log.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/errfuns.h"
static void *MoveMemoryIntervals(struct MemoryInterval *d,
const struct MemoryInterval *s, int n) {
// asan runtime depends on this function
int i;
assert(n >= 0);
if (d > s) {
for (i = n; i--;) {
d[i] = s[i];
}
} else {
for (i = 0; i < n; ++i) {
d[i] = s[i];
}
}
return d;
}
static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) {
// asan runtime depends on this function
assert(i >= 0);
assert(i + n <= mm->i);
MoveMemoryIntervals(mm->p + i, mm->p + i + n, mm->i - (i + n));
mm->i -= n;
}
static bool ExtendMemoryIntervals(struct MemoryIntervals *mm) {
int prot, flags;
char *base, *shad;
size_t gran, size;
struct DirectMap dm;
gran = kMemtrackGran;
base = (char *)kMemtrackStart;
prot = PROT_READ | PROT_WRITE;
flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED;
// TODO(jart): These map handles should not leak across NT fork()
if (mm->p == mm->s) {
if (IsAsan()) {
shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000);
dm = sys_mmap(shad, gran >> 3, prot, flags, -1, 0);
if (!dm.addr) return false;
}
dm = sys_mmap(base, gran, prot, flags, -1, 0);
if (!dm.addr) return false;
MoveMemoryIntervals(dm.addr, mm->p, mm->i);
mm->p = dm.addr;
mm->n = gran / sizeof(*mm->p);
} else {
size = ROUNDUP(mm->n * sizeof(*mm->p), gran);
base += size;
if (IsAsan()) {
shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000);
dm = sys_mmap(shad, gran >> 3, prot, flags, -1, 0);
if (!dm.addr) return false;
}
dm = sys_mmap(base, gran, prot, flags, -1, 0);
if (!dm.addr) return false;
mm->n = (size + gran) / sizeof(*mm->p);
}
#if IsModeDbg()
assert(AreMemoryIntervalsOk(mm));
#endif
return true;
}
int CreateMemoryInterval(struct MemoryIntervals *mm, int i) {
// asan runtime depends on this function
int rc;
rc = 0;
assert(i >= 0);
assert(i <= mm->i);
assert(mm->n >= 0);
if (UNLIKELY(mm->i == mm->n) && !ExtendMemoryIntervals(mm)) return enomem();
MoveMemoryIntervals(mm->p + i + 1, mm->p + i, mm->i++ - i);
return 0;
}
static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) {
if (CreateMemoryInterval(mm, i) == -1) return -1;
mm->p[i + 0].size -= (size_t)(mm->p[i + 0].y - (x - 1)) * FRAMESIZE;
mm->p[i + 0].y = x - 1;
mm->p[i + 1].size -= (size_t)((y + 1) - mm->p[i + 1].x) * FRAMESIZE;
mm->p[i + 1].x = y + 1;
return 0;
}
int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y,
void wf(struct MemoryIntervals *, int, int)) {
unsigned l, r;
#if IsModeDbg()
assert(y >= x);
assert(AreMemoryIntervalsOk(mm));
#endif
if (!mm->i) return 0;
// binary search for the lefthand side
l = FindMemoryInterval(mm, x);
if (l == mm->i) return 0;
if (y < mm->p[l].x) return 0;
// binary search for the righthand side
r = FindMemoryInterval(mm, y);
if (r == mm->i || (r > l && y < mm->p[r].x)) --r;
assert(r >= l);
assert(x <= mm->p[r].y);
// remove the middle of an existing map
//
// ----|mmmmmmmmmmmmmmmm|--------- before
// xxxxx
// ----|mmmm|-----|mmmmm|--------- after
//
// this isn't possible on windows because we track each
// 64kb segment on that platform using a separate entry
if (l == r && x > mm->p[l].x && y < mm->p[l].y) {
return PunchHole(mm, x, y, l);
}
// trim the right side of the lefthand map
//
// ----|mmmmmmm|-------------- before
// xxxxx
// ----|mmmm|----------------- after
//
if (x > mm->p[l].x && x <= mm->p[l].y) {
assert(y >= mm->p[l].y);
if (IsWindows()) return einval();
mm->p[l].size -= (size_t)(mm->p[l].y - (x - 1)) * FRAMESIZE;
mm->p[l].y = x - 1;
assert(mm->p[l].x <= mm->p[l].y);
++l;
}
// trim the left side of the righthand map
//
// ------------|mmmmm|-------- before
// xxxxx
// ---------------|mm|-------- after
//
if (y >= mm->p[r].x && y < mm->p[r].y) {
assert(x <= mm->p[r].x);
if (IsWindows()) return einval();
mm->p[r].size -= (size_t)((y + 1) - mm->p[r].x) * FRAMESIZE;
mm->p[r].x = y + 1;
assert(mm->p[r].x <= mm->p[r].y);
--r;
}
if (l <= r) {
if (IsWindows() && wf) {
wf(mm, l, r);
}
RemoveMemoryIntervals(mm, l, r - l + 1);
}
return 0;
}
int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h,
int prot, int flags, bool readonlyfile, bool iscow,
long offset, long size) {
// asan runtime depends on this function
unsigned i;
#if IsModeDbg()
assert(y >= x);
assert(AreMemoryIntervalsOk(mm));
#endif
i = FindMemoryInterval(mm, x);
// try to extend the righthand side of the lefthand entry
// we can't do that if we're tracking independent handles
// we can't do that if it's a file map with a small size!
if (i && x == mm->p[i - 1].y + 1 && h == mm->p[i - 1].h &&
prot == mm->p[i - 1].prot && flags == mm->p[i - 1].flags &&
mm->p[i - 1].size ==
(size_t)(mm->p[i - 1].y - mm->p[i - 1].x) * FRAMESIZE + FRAMESIZE) {
mm->p[i - 1].size += (size_t)(y - mm->p[i - 1].y) * FRAMESIZE;
mm->p[i - 1].y = y;
// if we filled the hole then merge the two mappings
if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h &&
prot == mm->p[i].prot && flags == mm->p[i].flags) {
mm->p[i - 1].y = mm->p[i].y;
mm->p[i - 1].size += mm->p[i].size;
RemoveMemoryIntervals(mm, i, 1);
}
}
// try to extend the lefthand side of the righthand entry
// we can't do that if we're creating a smaller file map!
else if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h &&
prot == mm->p[i].prot && flags == mm->p[i].flags &&
size == (size_t)(y - x) * FRAMESIZE + FRAMESIZE) {
mm->p[i].size += (size_t)(mm->p[i].x - x) * FRAMESIZE;
mm->p[i].x = x;
}
// otherwise, create a new entry and memmove the items
else {
if (CreateMemoryInterval(mm, i) == -1) return -1;
mm->p[i].x = x;
mm->p[i].y = y;
mm->p[i].h = h;
mm->p[i].prot = prot;
mm->p[i].flags = flags;
mm->p[i].offset = offset;
mm->p[i].size = size;
mm->p[i].iscow = iscow;
mm->p[i].readonlyfile = readonlyfile;
}
return 0;
}

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

View file

@ -1,7 +1,7 @@
/*-*- 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 2020 Justine Alexandra Roberts Tunney
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
@ -16,36 +16,41 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/strace.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/memtrack.internal.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
noasan bool AreMemoryIntervalsOk(const struct MemoryIntervals *mm) {
/* asan runtime depends on this function */
int i;
for (i = 0; i < mm->i; ++i) {
if (mm->p[i].y < mm->p[i].x) {
STRACE("AreMemoryIntervalsOk() y should be >= x!");
return false;
}
if (!(mm->p[i].size <=
(size_t)(mm->p[i].y - mm->p[i].x) * FRAMESIZE + FRAMESIZE &&
mm->p[i].size > (size_t)(mm->p[i].y - mm->p[i].x) * FRAMESIZE)) {
STRACE("AreMemoryIntervalsOk() size is wrong!");
return false;
}
if (i) {
if (mm->p[i].h != -1 || mm->p[i - 1].h != -1) {
if (mm->p[i].x <= mm->p[i - 1].y) {
return false;
}
} else {
if (!(mm->p[i - 1].y + 1 <= mm->p[i].x)) {
STRACE("AreMemoryIntervalsOk() out of order or overlap!");
return false;
}
}
}
}
return true;
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];