Make more fixes and improvements

- Remove PAGESIZE constant
- Fix realloc() documentation
- Fix ttyname_r() error reporting
- Make forking more reliable on Windows
- Make execvp() a few microseconds faster
- Make system() a few microseconds faster
- Tighten up the socket-related magic numbers
- Loosen restrictions on mmap() offset alignment
- Improve GetProgramExecutableName() with getenv("_")
- Use mkstemp() as basis for mktemp(), tmpfile(), tmpfd()
- Fix flakes in pthread_cancel_test, unix_test, fork_test
- Fix recently introduced futex stack overflow regression
- Let sockets be passed as stdio to subprocesses on Windows
- Improve security of bind() on Windows w/ SO_EXCLUSIVEADDRUSE
This commit is contained in:
Justine Tunney 2023-07-29 18:44:15 -07:00
parent 140a8a52e5
commit 18bb5888e1
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
311 changed files with 1239 additions and 2622 deletions

View file

@ -644,7 +644,7 @@ errno_t clone(void *func, void *stk, size_t stksz, int flags, void *arg,
if (!func) {
rc = EINVAL;
} else if (!IsTiny() &&
((flags & CLONE_VM) && (stksz < PAGESIZE || (stksz & 15)))) {
((flags & CLONE_VM) && (stksz < 4096 || (stksz & 15)))) {
rc = EINVAL;
} else if (IsAsan() &&
(((flags & CLONE_SETTLS) && !__asan_is_valid(tls, 64)) ||

View file

@ -148,7 +148,7 @@ textstartup void cosmo(long *sp, struct Syslib *m1) {
#endif
// initialize file system
__init_fds();
__init_fds(argc, argv, envp);
// set helpful globals
__argc = argc;

View file

@ -102,11 +102,11 @@ textstartup void __enable_tls(void) {
// Here's the layout we're currently using:
//
// .balign PAGESIZE
// .balign 4096
// _tdata_start:
// .tdata
// _tdata_size = . - _tdata_start
// .balign PAGESIZE
// .balign 4096
// _tbss_start:
// _tdata_start + _tbss_offset:
// .tbss

View file

@ -52,6 +52,7 @@
#include "libc/runtime/memtrack.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
#include "libc/sock/internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/o.h"
@ -68,7 +69,9 @@ bool32 __onntconsoleevent_nt(uint32_t);
void kmalloc_unlock(void);
static textwindows wontreturn void AbortFork(const char *func) {
STRACE("fork() %s() failed %d", func, GetLastError());
#ifdef SYSDEBUG
kprintf("fork() %s() failed with win32 error %d\n", func, GetLastError());
#endif
ExitProcess(177);
}
@ -104,7 +107,15 @@ static dontinline textwindows bool ForkIo2(int64_t h, void *buf, size_t n,
}
static dontinline textwindows bool WriteAll(int64_t h, void *buf, size_t n) {
return ForkIo2(h, buf, n, WriteFile, "WriteFile", false);
bool ok;
ok = ForkIo2(h, buf, n, WriteFile, "WriteFile", false);
#ifdef SYSDEBUG
if (!ok) {
kprintf("failed to write %zu bytes to forked child: %d\n", n,
GetLastError());
}
#endif
return ok;
}
static textwindows dontinline void ReadOrDie(int64_t h, void *buf, size_t n) {
@ -320,8 +331,13 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
}
for (i = 0; i < _mmi.i && ok; ++i) {
if ((_mmi.p[i].flags & MAP_TYPE) != MAP_SHARED) {
ok = WriteAll(writer, (void *)((uint64_t)_mmi.p[i].x << 16),
_mmi.p[i].size);
uint32_t op;
char *p = (char *)((uint64_t)_mmi.p[i].x << 16);
// XXX: forking destroys thread guard pages currently
VirtualProtect(
p, _mmi.p[i].size,
__prot2nt(_mmi.p[i].prot | PROT_READ, _mmi.p[i].iscow), &op);
ok = WriteAll(writer, p, _mmi.p[i].size);
}
}
if (ok) ok = WriteAll(writer, __data_start, __data_end - __data_start);

View file

@ -51,7 +51,7 @@ int __inflate(void *, size_t, const void *, size_t);
void *_Mmap(void *, size_t, int, int, int, int64_t) dontasan;
int _Munmap(char *, size_t) dontasan;
void __on_arithmetic_overflow(void);
void __init_fds(void);
void __init_fds(int, char **, char **);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -141,10 +141,6 @@ forceinline pureconst bool IsShadowFrame(int x) {
return 0x7fff <= x && x < 0x10008000;
}
forceinline pureconst bool IsArenaFrame(int x) {
return 0x5004 <= x && x <= 0x7ffb;
}
forceinline pureconst bool IsStaticStackFrame(int x) {
intptr_t stack = GetStaticStackAddr(0);
return (int)(stack >> 16) <= x &&
@ -192,19 +188,6 @@ forceinline pureconst bool OverlapsImageSpace(const void *p, size_t n) {
}
}
forceinline pureconst bool OverlapsArenaSpace(const void *p, size_t n) {
intptr_t BegA, EndA, BegB, EndB;
if (n) {
BegA = (intptr_t)p;
EndA = BegA + (n - 1);
BegB = 0x50000000;
EndB = 0x7ffdffff;
return MAX(BegA, BegB) < MIN(EndA, EndB);
} else {
return 0;
}
}
forceinline pureconst bool OverlapsShadowSpace(const void *p, size_t n) {
intptr_t BegA, EndA, BegB, EndB;
if (n) {

View file

@ -1,54 +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/calls/calls.h"
#include "libc/intrin/bits.h"
#include "libc/stdio/rand.h"
#include "libc/stdio/temp.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
/**
* Creates temporary file name and descriptor, e.g.
*
* char path[PATH_MAX+1];
* strlcat(path, kTmpDir, sizeof(path));
* strlcat(path, "sauce.XXXXXX", sizeof(path));
* close(mkstemp(path));
* puts(path);
*
* @param template is mutated to replace last six X's with rng
* @return open file descriptor r + w exclusive or -1 w/ errno
* @raise EINVAL if `template` didn't end with `XXXXXX`
*/
int mkstemp(char *template) {
int i, n;
uint64_t w;
if ((n = strlen(template)) < 6 ||
READ16LE(template + n - 2) != READ16LE("XX") ||
READ32LE(template + n - 6) != READ32LE("XXXX")) {
return einval();
}
w = _rand64();
for (i = 0; i < 6; ++i) {
template[n - 6 + i] = "0123456789abcdefghijklmnopqrstuvwxyz"[w % 36];
w /= 36;
}
return open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
}

View file

@ -245,9 +245,10 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
int fd, int64_t off) {
char *p = addr;
struct DirectMap dm;
size_t requested_size;
int a, b, i, f, m, n, x;
bool needguard, clashes;
unsigned long guardsize;
unsigned long page_size;
size_t virtualused, virtualneed;
if (VERY_UNLIKELY(!size)) {
@ -274,11 +275,12 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
flags = MAP_SHARED; // cf. MAP_SHARED_VALIDATE
}
requested_size = size;
page_size = getauxval(AT_PAGESZ);
if (flags & MAP_ANONYMOUS) {
fd = -1;
off = 0;
size = ROUNDUP(size, FRAMESIZE);
if (IsWindows()) prot |= PROT_WRITE; // kludge
if ((flags & MAP_TYPE) == MAP_FILE) {
STRACE("need MAP_PRIVATE or MAP_SHARED");
return VIP(einval());
@ -289,8 +291,8 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
} else if (VERY_UNLIKELY(off < 0)) {
STRACE("mmap negative offset");
return VIP(einval());
} else if (VERY_UNLIKELY(!ALIGNED(off))) {
STRACE("mmap off isn't 64kb aligned");
} else if (off & ((IsWindows() ? FRAMESIZE : page_size) - 1)) {
STRACE("mmap offset isn't properly aligned");
return VIP(einval());
} else if (VERY_UNLIKELY(INT64_MAX - size < off)) {
STRACE("mmap too large");
@ -326,8 +328,7 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
OnUnrecoverableMmapError("FIXED UNTRACK FAILED");
}
}
} else if (p && !clashes && !OverlapsArenaSpace(p, size) &&
!OverlapsShadowSpace(p, size)) {
} else if (p && !clashes && !OverlapsShadowSpace(p, size)) {
x = FRAME(p);
} else if (!Automap(n, a, &x)) {
STRACE("automap has no room for %d frames with %d alignment", n, a);
@ -335,7 +336,6 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
}
needguard = false;
guardsize = getauxval(AT_PAGESZ);
p = (char *)ADDR_32_TO_48(x);
if ((f & MAP_TYPE) == MAP_STACK) {
if (~f & MAP_ANONYMOUS) {
@ -374,11 +374,15 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
if ((dm = sys_mmap(p + size - SIGSTKSZ, SIGSTKSZ, prot,
f | MAP_GROWSDOWN_linux, fd, off))
.addr != MAP_FAILED) {
npassert(sys_mmap(p, guardsize, PROT_NONE,
npassert(sys_mmap(p, page_size, PROT_NONE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)
.addr == p);
dm.addr = p;
return FinishMemory(p, size, prot, flags, fd, off, f, x, n, dm);
p = FinishMemory(p, size, prot, flags, fd, off, f, x, n, dm);
if (IsAsan() && p != MAP_FAILED) {
__asan_poison(p, page_size, kAsanStackOverflow);
}
return p;
} else if (errno == ENOTSUP) {
// WSL doesn't support MAP_GROWSDOWN
needguard = true;
@ -399,14 +403,14 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
}
if (p != MAP_FAILED) {
if (IsAsan()) {
__asan_poison(p + requested_size, size - requested_size,
kAsanMmapSizeOverrun);
}
if (needguard) {
if (!IsWindows()) {
// make windows fork() code simpler
mprotect(p, guardsize, PROT_NONE);
}
unassert(!mprotect(p, page_size, PROT_NONE));
if (IsAsan()) {
__repstosb((void *)(((intptr_t)p >> 3) + 0x7fff8000),
kAsanStackOverflow, guardsize / 8);
__asan_poison(p, page_size, kAsanStackOverflow);
}
}
}
@ -415,7 +419,7 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
}
/**
* Beseeches system for page-table entries, e.g.
* Creates virtual memory, e.g.
*
* char *m;
* m = mmap(NULL, FRAMESIZE, PROT_READ | PROT_WRITE,
@ -430,9 +434,7 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
* needs to be made mandatory because of Windows although you can
* use __sys_mmap() to circumvent it on System Five in which case
* runtime support services, e.g. asan memory safety, could break
* @param size must be >0 and needn't be a multiple of FRAMESIZE, but
* will be rounded up to FRAMESIZE automatically if MAP_ANONYMOUS
* is specified
* @param size must be >0 otherwise EINVAL is raised
* @param prot can have PROT_READ/PROT_WRITE/PROT_EXEC/PROT_NONE/etc.
* @param flags should have one of the following masked by `MAP_TYPE`
* - `MAP_FILE` in which case `MAP_ANONYMOUS` shouldn't be used
@ -459,8 +461,8 @@ dontasan inline void *_Mmap(void *addr, size_t size, int prot, int flags,
* @param fd is an open()'d file descriptor, whose contents shall be
* made available w/ automatic reading at the chosen address
* @param off specifies absolute byte index of fd's file for mapping,
* should be zero if MAP_ANONYMOUS is specified, and sadly needs
* to be 64kb aligned too
* should be zero if MAP_ANONYMOUS is specified, which SHOULD be
* aligned to FRAMESIZE
* @return virtual base address of new mapping, or MAP_FAILED w/ errno
*/
void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) {

View file

@ -27,6 +27,7 @@ textwindows int sys_mprotect_nt(void *addr, size_t size, int prot) {
uint32_t op;
char *a, *b, *x, *y, *p;
__mmi_lock();
size = (size + 4095) & -4096;
p = addr;
i = FindMemoryInterval(&_mmi, (intptr_t)p >> 16);
if (i == _mmi.i || (!i && p + size <= (char *)ADDR_32_TO_48(_mmi.p[0].x))) {
@ -41,9 +42,14 @@ textwindows int sys_mprotect_nt(void *addr, size_t size, int prot) {
// we unfortunately must do something similar to this for cow
for (; i < _mmi.i; ++i) {
x = (char *)ADDR_32_TO_48(_mmi.p[i].x);
y = x + _mmi.p[i].size;
y = (char *)ADDR_32_TO_48(_mmi.p[i].y) + 65536;
if ((x <= p && p < y) || (x < p + size && p + size <= y) ||
(p < x && y < p + size)) {
if (p <= x && p + size >= y) {
_mmi.p[i].prot = prot;
} else {
_mmi.p[i].prot |= prot;
}
a = MIN(MAX(p, x), y);
b = MAX(MIN(p + size, y), x);
if (!VirtualProtect(a, b - a, __prot2nt(prot, _mmi.p[i].iscow), &op)) {

View file

@ -23,6 +23,8 @@
#include "libc/intrin/likely.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/auxv.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h"
@ -41,7 +43,7 @@ int mprotect(void *addr, size_t size, int prot) {
rc = einval(); // unix checks prot before checking size
} else if (!size) {
return 0; // make new technology consistent with unix
} else if (UNLIKELY((intptr_t)addr & 4095)) {
} else if (UNLIKELY((intptr_t)addr & (getauxval(AT_PAGESZ) - 1))) {
rc = einval(); // unix checks prot before checking size
} else if (!IsWindows()) {
rc = sys_mprotect(addr, size, prot);