From 4e56d89dcdf227070b42aa684a0817874589dde2 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 3 Feb 2021 06:22:51 -0800 Subject: [PATCH] Eliminate some flakes - Get ASAN working on Windows. - Deleting directories and then recreating them with the same name in a short period of time appears to be a no-no on Windows. - There's no reason to call FlushFileBuffers on close() for pipes, and it's harmful since it might block indefinitely for no good reason. --- Makefile | 42 +-- libc/calls/calls.mk | 1 - libc/calls/close-nt.c | 8 +- libc/calls/close.c | 3 +- libc/calls/dup-nt.c | 20 +- libc/calls/{growfds.c => ensurefds.c} | 38 ++- libc/calls/internal.h | 4 +- libc/calls/ioctl-tcsets-nt.c | 1 - libc/calls/isdebuggerpresent.c | 15 +- libc/calls/mkntcmdline.c | 4 +- libc/calls/mkntenvblock.c | 25 +- libc/calls/open-nt.c | 13 +- libc/calls/open.c | 2 +- libc/calls/openanon.c | 5 +- libc/calls/pipe-nt.c | 8 +- libc/calls/releasefd.c | 29 ++ libc/calls/{getemptyfd.c => reservefd.c} | 15 +- libc/calls/sigaction.c | 26 +- libc/calls/sigenter.S | 2 +- libc/calls/winerr.greg.c | 2 +- libc/calls/write-nt.c | 1 - libc/integral/c.inc | 2 +- libc/intrin/asan.c | 55 ++-- libc/intrin/intrin.mk | 4 +- libc/intrin/ntgetversion.c | 24 ++ libc/intrin/somanyasan.S | 129 +++++--- libc/nt/struct/teb.h | 17 +- libc/rand/rand.mk | 8 +- libc/rand/rand64.c | 2 +- libc/rand/winrandish.c | 4 +- libc/runtime/arememoryintervalsok.c | 2 +- libc/runtime/assertfail.c | 79 ++++- libc/runtime/cxaatexit.c | 4 +- libc/runtime/directmap.c | 4 +- libc/runtime/directmapnt.c | 5 +- libc/runtime/findmemoryinterval.c | 2 +- libc/runtime/fork-nt.c | 22 +- libc/runtime/getdosargv.c | 8 +- libc/runtime/getdosenviron.c | 30 +- libc/runtime/memtrack.c | 19 +- libc/runtime/memtrack.h | 1 - libc/runtime/memtracknt.c | 4 +- libc/runtime/print.greg.c | 1 - libc/runtime/runtime.h | 1 + libc/runtime/stackchkfail.c | 3 +- libc/runtime/winmain.greg.c | 13 +- libc/sock/accept-nt.c | 4 +- libc/sock/epoll.c | 11 +- libc/sock/kntwsadata.c | 7 +- libc/sock/socket-nt.c | 2 +- libc/stubs/asan.S | 388 ----------------------- libc/testlib/testlib.h | 117 ++++--- libc/testlib/testrunner.c | 24 +- test/libc/calls/commandv_test.c | 11 +- test/libc/calls/fcntl_test.c | 15 +- test/libc/calls/getcwd_test.c | 15 +- test/libc/calls/lseek_test.c | 13 +- test/libc/calls/mkdir_test.c | 13 +- test/libc/runtime/mmap_test.c | 8 +- tool/viz/printpeb.c | 4 +- 60 files changed, 588 insertions(+), 751 deletions(-) rename libc/calls/{growfds.c => ensurefds.c} (76%) create mode 100644 libc/calls/releasefd.c rename libc/calls/{getemptyfd.c => reservefd.c} (88%) create mode 100644 libc/intrin/ntgetversion.c delete mode 100644 libc/stubs/asan.S diff --git a/Makefile b/Makefile index 37cf9490d..a133bdc5b 100644 --- a/Makefile +++ b/Makefile @@ -82,17 +82,19 @@ o: o/$(MODE)/ape \ PKGS = --include ~/.cosmo.mk #──No.1 +-include ~/.cosmo.mk include build/functions.mk #─┐ -include build/definitions.mk # ├──meta -include build/config.mk # │ -include build/rules.mk # │ +include build/definitions.mk # ├──META +include build/config.mk # │ You can build +include build/rules.mk # │ You can topologically order include build/online.mk # │ include libc/stubs/stubs.mk #─┘ include libc/nexgen32e/nexgen32e.mk #─┐ -include libc/intrin/intrin.mk # │ -include libc/linux/linux.mk # │ -include libc/tinymath/tinymath.mk # ├──metal +include libc/sysv/sysv.mk # ├──SYSTEM SUPPORT +include libc/nt/nt.mk # │ You can do math +include libc/intrin/intrin.mk # │ You can use the stack +include libc/linux/linux.mk # │ You can manipulate arrays +include libc/tinymath/tinymath.mk # │ You can issue raw system calls include third_party/compiler_rt/compiler_rt.mk # │ include libc/bits/bits.mk # │ include libc/str/str.mk # │ @@ -100,20 +102,18 @@ include third_party/xed/xed.mk # │ include third_party/zlib/zlib.mk # │ include libc/elf/elf.mk # │ include ape/lib/apelib.mk # │ -include ape/ape.mk #─┘ -include libc/sysv/sysv.mk #─┐ -include libc/nt/nt.mk # ├──system -include libc/fmt/fmt.mk # │ -include libc/rand/rand.mk #─┘ +include ape/ape.mk # │ +include libc/fmt/fmt.mk #─┘ include libc/calls/calls.mk #─┐ -include libc/runtime/runtime.mk # ├──systems -include libc/crt/crt.mk # │ +include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME +include libc/crt/crt.mk # │ You can issue system calls +include libc/rand/rand.mk # │ include libc/unicode/unicode.mk # │ -include third_party/dlmalloc/dlmalloc.mk # │ -include libc/mem/mem.mk # │ -include libc/ohmyplus/ohmyplus.mk # │ -include libc/zipos/zipos.mk # │ -include third_party/gdtoa/gdtoa.mk # │ +include third_party/dlmalloc/dlmalloc.mk #─┘ +include libc/mem/mem.mk #─┐ +include libc/ohmyplus/ohmyplus.mk # ├──DYNAMIC RUNTIME +include libc/zipos/zipos.mk # │ You can now use stdio +include third_party/gdtoa/gdtoa.mk # │ You can finally call malloc() include libc/time/time.mk # │ include libc/alg/alg.mk # │ include libc/stdio/stdio.mk # │ @@ -131,8 +131,8 @@ include third_party/musl/musl.mk # │ include third_party/getopt/getopt.mk # │ include libc/libc.mk #─┘ include libc/sock/sock.mk #─┐ -include dsp/tty/tty.mk # ├──online -include libc/dns/dns.mk # │ +include dsp/tty/tty.mk # ├──ONLINE RUNTIME +include libc/dns/dns.mk # │ You can communicate with the network include libc/crypto/crypto.mk # │ include net/http/http.mk #─┘ include third_party/lemon/lemon.mk diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index ec5993a26..ae2afe0e1 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -44,7 +44,6 @@ LIBC_CALLS_A_DIRECTDEPS = \ LIBC_NT_ADVAPI32 \ LIBC_NT_KERNEL32 \ LIBC_NT_NTDLL \ - LIBC_RAND \ LIBC_STR \ LIBC_STUBS \ LIBC_SYSV_CALLS \ diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index b477f86dd..5c500b0b4 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -17,13 +17,15 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" +#include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/sysv/errfuns.h" textwindows int close$nt(int fd) { bool32 ok; - if (g_fds.p[fd].kind == kFdFile) { + if (g_fds.p[fd].kind == kFdFile && + GetFileType(g_fds.p[fd].handle) == kNtFileTypeDisk) { /* * Like Linux, closing a file on Windows doesn't guarantee it's * immediately synced to disk. But unlike Linux, this could cause @@ -32,6 +34,8 @@ textwindows int close$nt(int fd) { FlushFileBuffers(g_fds.p[fd].handle); } ok = CloseHandle(g_fds.p[fd].handle); - if (g_fds.p[fd].kind == kFdConsole) ok &= CloseHandle(g_fds.p[fd].extra); + if (g_fds.p[fd].kind == kFdConsole) { + ok &= CloseHandle(g_fds.p[fd].extra); + } return ok ? 0 : __winerr(); } diff --git a/libc/calls/close.c b/libc/calls/close.c index 3b7b83c07..b5e83c52a 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -53,8 +53,7 @@ int close(int fd) { rc = ebadf(); } if (!__vforked && fd < g_fds.n) { - g_fds.p[fd].kind = kFdEmpty; - g_fds.f = MIN(g_fds.f, fd); + __releasefd(fd); } return rc; } diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 33b4abaed..1e8437009 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -30,17 +30,18 @@ */ textwindows int dup$nt(int oldfd, int newfd, int flags) { int64_t proc; - if (!__isfdkind(oldfd, kFdFile)) return ebadf(); + if (oldfd < 0) return einval(); + if (oldfd >= g_fds.n || + (g_fds.p[oldfd].kind != kFdFile && g_fds.p[oldfd].kind != kFdSocket && + g_fds.p[oldfd].kind != kFdConsole)) { + return ebadf(); + } if (newfd == -1) { - if ((newfd = __getemptyfd()) == -1) { - return -1; - } - } else if (__ensurefds(newfd) != -1) { - if (g_fds.p[newfd].kind != kFdEmpty) { - close(newfd); - } + if ((newfd = __reservefd()) == -1) return -1; } else { - return -1; + if (__ensurefds(newfd) == -1) return -1; + if (g_fds.p[newfd].kind) close(newfd); + g_fds.p[newfd].kind = kFdReserved; } proc = GetCurrentProcess(); if (DuplicateHandle(proc, g_fds.p[oldfd].handle, proc, &g_fds.p[newfd].handle, @@ -49,6 +50,7 @@ textwindows int dup$nt(int oldfd, int newfd, int flags) { g_fds.p[newfd].flags = flags; return newfd; } else { + __releasefd(newfd); return __winerr(); } } diff --git a/libc/calls/growfds.c b/libc/calls/ensurefds.c similarity index 76% rename from libc/calls/growfds.c rename to libc/calls/ensurefds.c index d1503801c..1114bddc4 100644 --- a/libc/calls/growfds.c +++ b/libc/calls/ensurefds.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/mem/mem.h" @@ -24,24 +25,29 @@ #include "libc/sysv/errfuns.h" int __ensurefds(int fd) { - size_t n; - struct Fd *p; - if (fd < g_fds.n) return fd; - if (weaken(malloc)) { - n = MAX(fd + 1, g_fds.n + (g_fds.n << 1)); - if ((p = weaken(malloc)(n * sizeof(*p)))) { - memcpy(p, g_fds.p, g_fds.n * sizeof(*p)); - memset(p + g_fds.n, 0, (n - g_fds.n) * sizeof(*p)); - if (g_fds.p != g_fds.__init_p && weaken(free)) { - weaken(free)(g_fds.p); + size_t n1, n2; + struct Fd *p1, *p2; + for (;;) { + p1 = g_fds.p; + n1 = g_fds.n; + if (fd < n1) return fd; + if (weaken(malloc)) { + n2 = MAX(fd + 1, n1 + (n1 << 1)); + if ((p2 = weaken(malloc)(n2 * sizeof(*p1)))) { + memcpy(p2, p1, n1 * sizeof(*p1)); + memset(p2 + n1, 0, (n2 - n1) * sizeof(*p1)); + if (p1 != g_fds.__init_p && weaken(free)) weaken(free)(p1); + if (cmpxchg(&g_fds.p, p1, p2)) { + g_fds.n = n2; + return fd; + } else if (weaken(free)) { + weaken(free)(p2); + } + } else { + return enomem(); } - g_fds.p = p; - g_fds.n = n; - return fd; } else { - return enomem(); + return emfile(); } - } else { - return emfile(); } } diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 195d5acf9..c7eae122e 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -52,6 +52,7 @@ struct Fds { kFdSerial, kFdZip, kFdEpoll, + kFdReserved, } kind; unsigned flags; } * p; @@ -68,7 +69,8 @@ hidden extern struct NtSystemInfo g_ntsysteminfo; hidden extern struct NtStartupInfo g_ntstartupinfo; hidden extern const struct NtSecurityAttributes kNtIsInheritable; -ssize_t __getemptyfd(void) hidden; +int __reservefd(void) hidden; +void __releasefd(int) hidden; int __ensurefds(int) hidden; void __removefd(int) hidden; diff --git a/libc/calls/ioctl-tcsets-nt.c b/libc/calls/ioctl-tcsets-nt.c index 1d0fc0de3..abe4052c0 100644 --- a/libc/calls/ioctl-tcsets-nt.c +++ b/libc/calls/ioctl-tcsets-nt.c @@ -22,7 +22,6 @@ #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" #include "libc/nt/enum/version.h" -#include "libc/nt/struct/teb.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/isdebuggerpresent.c b/libc/calls/isdebuggerpresent.c index 54c9b1c6c..844d128b1 100644 --- a/libc/calls/isdebuggerpresent.c +++ b/libc/calls/isdebuggerpresent.c @@ -32,7 +32,11 @@ #define kBufSize 1024 #define kProcStatus "/proc/self/status" -_Alignas(16) static const char kGdbPid[] = "TracerPid:\t"; +#define kPid "TracerPid:\t" + +static noasan int NtBeingDebugged(void) { + return NtGetPeb()->BeingDebugged; +} /** * Determines if gdb, strace, windbg, etc. is controlling process. @@ -42,19 +46,18 @@ int IsDebuggerPresent(bool force) { int fd, res; ssize_t got; char buf[1024]; - res = 0; + res = false; if (!force) { if (getenv("HEISENDEBUG")) return false; if (IsGenuineCosmo()) return false; } if (IsWindows()) { - res = NtGetPeb()->BeingDebugged; + res = NtBeingDebugged(); } else if (IsLinux()) { if ((fd = openat$sysv(AT_FDCWD, kProcStatus, O_RDONLY, 0)) != -1) { - if ((got = read$sysv(fd, buf, sizeof(buf) - sizeof(kGdbPid))) != -1) { + if ((got = read$sysv(fd, buf, sizeof(buf) - sizeof(kPid))) != -1) { buf[got] = '\0'; - res = - atoi(firstnonnull(strstr(buf, kGdbPid), kGdbPid) + strlen(kGdbPid)); + res = atoi(firstnonnull(strstr(buf, kPid), kPid) + strlen(kPid)); } close$sysv(fd); } diff --git a/libc/calls/mkntcmdline.c b/libc/calls/mkntcmdline.c index 1339cdcfb..eb52c3626 100644 --- a/libc/calls/mkntcmdline.c +++ b/libc/calls/mkntcmdline.c @@ -37,8 +37,8 @@ * @kudos Daniel Colascione for teaching how to quote * @see libc/runtime/dosargv.c */ -textwindows int mkntcmdline(char16_t cmdline[ARG_MAX], const char *prog, - char *const argv[]) { +textwindows noasan int mkntcmdline(char16_t cmdline[ARG_MAX], const char *prog, + char *const argv[]) { char *arg; uint64_t w; wint_t x, y; diff --git a/libc/calls/mkntenvblock.c b/libc/calls/mkntenvblock.c index beae3b238..4fb63e6b1 100644 --- a/libc/calls/mkntenvblock.c +++ b/libc/calls/mkntenvblock.c @@ -29,21 +29,18 @@ #include "libc/str/utf16.h" #include "libc/sysv/errfuns.h" -static int CompareStrings(const char *l, const char *r) { +static noasan int CompareStrings(const char *l, const char *r) { size_t i = 0; while (l[i] == r[i] && r[i]) ++i; return (l[i] & 0xff) - (r[i] & 0xff); } -static void SortStrings(char **a, size_t n) { - char *t; - size_t i, j; - for (i = 1; i < n; ++i) { - for (t = a[i], j = i; j > 0 && CompareStrings(t, a[j - 1]) < 0; --j) { - a[j] = a[j - 1]; - } - a[j] = t; +static noasan void InsertString(char **a, size_t i, char *s) { + size_t j; + for (j = i; j > 0 && CompareStrings(s, a[j - 1]) < 0; --j) { + a[j] = a[j - 1]; } + a[j] = s; } /** @@ -57,8 +54,9 @@ static void SortStrings(char **a, size_t n) { * @return 0 on success, or -1 w/ errno * @error E2BIG if total number of shorts exceeded ARG_MAX (0x8000) */ -textwindows int mkntenvblock(char16_t envvars[ARG_MAX], char *const envp[], - const char *extravar) { +textwindows noasan int mkntenvblock(char16_t envvars[ARG_MAX], + char *const envp[], const char *extravar) { + char *t; axdx_t rc; uint64_t w; char **vars; @@ -66,9 +64,8 @@ textwindows int mkntenvblock(char16_t envvars[ARG_MAX], char *const envp[], size_t i, j, k, n, m; for (n = 0; envp[n];) n++; vars = alloca((n + 1) * sizeof(char *)); - memcpy(vars, envp, n * sizeof(char *)); - if (extravar) vars[n++] = extravar; - SortStrings(vars, n); + for (i = 0; i < n; ++i) InsertString(vars, i, envp[i]); + if (extravar) InsertString(vars, n++, extravar); for (k = i = 0; i < n; ++i) { j = 0; do { diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 7bde0dc0d..1a381b0c0 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -112,11 +112,16 @@ static textwindows ssize_t open$nt$file(int dirfd, const char *file, textwindows ssize_t open$nt(int dirfd, const char *file, uint32_t flags, int32_t mode) { - size_t fd; - if ((fd = __getemptyfd()) == -1) return -1; + int fd; + ssize_t rc; + if ((fd = __reservefd()) == -1) return -1; if ((flags & O_ACCMODE) == O_RDWR && !strcmp(file, kNtMagicPaths.devtty)) { - return open$nt$console(dirfd, &kNtMagicPaths, flags, mode, fd); + rc = open$nt$console(dirfd, &kNtMagicPaths, flags, mode, fd); } else { - return open$nt$file(dirfd, file, flags, mode, fd); + rc = open$nt$file(dirfd, file, flags, mode, fd); } + if (rc == -1) { + __releasefd(fd); + } + return rc; } diff --git a/libc/calls/open.c b/libc/calls/open.c index edf7c46b5..5f1e783ea 100644 --- a/libc/calls/open.c +++ b/libc/calls/open.c @@ -31,7 +31,7 @@ * @asyncsignalsafe * @vforksafe */ -nodiscard int open(const char *file, int flags, ...) { +int open(const char *file, int flags, ...) { va_list va; unsigned mode; va_start(va, flags); diff --git a/libc/calls/openanon.c b/libc/calls/openanon.c index a2ecae59b..f86061501 100644 --- a/libc/calls/openanon.c +++ b/libc/calls/openanon.c @@ -67,8 +67,8 @@ static int openanon$impl(const char *name, unsigned flags, } return fd; } else { - if ((fd = __getemptyfd()) != -1 && - (g_fds.p[fd].handle = CreateFileA( + if ((fd = __reservefd()) == -1) return -1; + if ((g_fds.p[fd].handle = CreateFileA( pathbuf, kNtGenericRead | kNtGenericWrite, kNtFileShareExclusive, &kNtIsInheritable, kNtCreateAlways, (kNtFileAttributeNotContentIndexed | kNtFileAttributeNormal | @@ -78,6 +78,7 @@ static int openanon$impl(const char *name, unsigned flags, g_fds.p[fd].flags = flags; return fd; } else { + __releasefd(fd); return __winerr(); } } diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 042d1172a..fad391c1e 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -58,16 +58,19 @@ textwindows int pipe$nt(int pipefd[2], unsigned flags) { int reader, writer; char16_t pipename[64]; CreatePipeName(pipename); + if ((reader = __reservefd()) == -1) return -1; + if ((writer = __reservefd()) == -1) { + __releasefd(reader); + return -1; + } if ((hin = CreateNamedPipe(pipename, kNtPipeAccessInbound, kNtPipeWait | kNtPipeReadmodeByte, 1, 65536, 65536, 0, &kNtIsInheritable)) != -1) { if ((hout = CreateFile(pipename, kNtGenericWrite, kNtFileShareWrite, &kNtIsInheritable, kNtOpenExisting, 0, 0)) != -1) { - reader = __getemptyfd(); g_fds.p[reader].kind = kFdFile; g_fds.p[reader].flags = flags; g_fds.p[reader].handle = hin; - writer = __getemptyfd(); g_fds.p[writer].kind = kFdFile; g_fds.p[writer].flags = flags; g_fds.p[writer].handle = hout; @@ -78,5 +81,6 @@ textwindows int pipe$nt(int pipefd[2], unsigned flags) { CloseHandle(hin); } } + __releasefd(reader); return __winerr(); } diff --git a/libc/calls/releasefd.c b/libc/calls/releasefd.c new file mode 100644 index 000000000..0577d7c76 --- /dev/null +++ b/libc/calls/releasefd.c @@ -0,0 +1,29 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/bits/bits.h" +#include "libc/calls/internal.h" + +void __releasefd(int fd) { + int x; + g_fds.p[fd].kind = kFdEmpty; + do { + x = g_fds.f; + if (fd >= x) break; + } while (!cmpxchg(&g_fds.f, x, fd)); +} diff --git a/libc/calls/getemptyfd.c b/libc/calls/reservefd.c similarity index 88% rename from libc/calls/getemptyfd.c rename to libc/calls/reservefd.c index 0a49c8386..d6b5ba473 100644 --- a/libc/calls/getemptyfd.c +++ b/libc/calls/reservefd.c @@ -24,11 +24,16 @@ /** * Finds open file descriptor slot. */ -ssize_t __getemptyfd(void) { - for (; g_fds.f < g_fds.n; ++g_fds.f) { - if (g_fds.p[g_fds.f].kind == kFdEmpty) { - return g_fds.f; +int __reservefd(void) { + int fd; + for (;;) { + fd = g_fds.f; + if (fd >= g_fds.n) { + if (__ensurefds(fd) == -1) return -1; + } + cmpxchg(&g_fds.f, fd, fd + 1); + if (cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) { + return fd; } } - return __ensurefds(g_fds.f); } diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 8ea675b3e..f23c7532e 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -19,6 +19,7 @@ #include "libc/bits/bits.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/sigbits.h" #include "libc/calls/struct/sigaction-freebsd.internal.h" #include "libc/calls/struct/sigaction-linux.internal.h" #include "libc/calls/struct/sigaction-openbsd.internal.h" @@ -128,9 +129,8 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { sizeof(struct sigaction) > sizeof(struct sigaction$openbsd)); int rc, rva, oldrva; struct sigaction *ap, copy; - if (!(0 < sig && sig < NSIG) || sig == SIGKILL || sig == SIGSTOP) { - return einval(); - } + if (!(0 < sig && sig < NSIG)) return einval(); + if (sig == SIGKILL || sig == SIGSTOP) return einval(); if (!act) { rva = (int32_t)(intptr_t)SIG_DFL; } else if ((intptr_t)act->sa_handler < kSigactionMinRva) { @@ -141,6 +141,9 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { } else { return efault(); } + if (__vforked && rva != (intptr_t)SIG_DFL && rva != (intptr_t)SIG_IGN) { + return einval(); + } if (!IsWindows()) { if (!IsMetal()) { if (act) { @@ -149,16 +152,13 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { if (IsXnu()) { ap->sa_restorer = (void *)&xnutrampoline; ap->sa_handler = (void *)&xnutrampoline; - } else { - if (IsLinux()) { - if (!(ap->sa_flags & SA_RESTORER)) { - ap->sa_flags |= SA_RESTORER; - ap->sa_restorer = &__restore_rt; - } - } - if (rva >= kSigactionMinRva) { - ap->sa_sigaction = (sigaction_f)__sigenter; + } else if (IsLinux()) { + if (!(ap->sa_flags & SA_RESTORER)) { + ap->sa_flags |= SA_RESTORER; + ap->sa_restorer = &__restore_rt; } + } else if (rva >= kSigactionMinRva) { + ap->sa_sigaction = (sigaction_f)__sigenter; } sigaction$cosmo2native((union metasigaction *)ap); } else { @@ -178,7 +178,7 @@ int(sigaction)(int sig, const struct sigaction *act, struct sigaction *oldact) { } rc = 0; } - if (rc != -1) { + if (rc != -1 && !__vforked) { if (oldact) { oldrva = __sighandrvas[sig]; oldact->sa_sigaction = (sigaction_f)( diff --git a/libc/calls/sigenter.S b/libc/calls/sigenter.S index 0a28fb5cc..a11a27ae6 100644 --- a/libc/calls/sigenter.S +++ b/libc/calls/sigenter.S @@ -20,7 +20,7 @@ #include "libc/macros.h" .source __FILE__ -/ System Five signal handler. +/ BSD signal handler. / / This is needed because (1) a signal is allowed to trigger at / just about any time, and leaf functions (e.g. memcpy) aren't diff --git a/libc/calls/winerr.greg.c b/libc/calls/winerr.greg.c index 1462f5829..5fc3e4e6a 100644 --- a/libc/calls/winerr.greg.c +++ b/libc/calls/winerr.greg.c @@ -28,7 +28,7 @@ * @return -1 w/ few exceptions * @note this is a code-size saving device */ -privileged int64_t __winerr(void) { +privileged noasan int64_t __winerr(void) { if (IsWindows()) { errno = GetLastError(); return -1; diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index a7e1d0818..bc414cd08 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -42,7 +42,6 @@ textwindows ssize_t write$nt(struct Fd *fd, const struct iovec *iov, iovlen ? clampio(iov[0].iov_len) : 0, &wrote, offset2overlap(opt_offset, &overlap))) { if (!wrote) assert(!SumIovecLen(iov, iovlen)); - FlushFileBuffers(fd->handle); return wrote; } else { return __winerr(); diff --git a/libc/integral/c.inc b/libc/integral/c.inc index 1a7e9dd85..24a287834 100644 --- a/libc/integral/c.inc +++ b/libc/integral/c.inc @@ -618,7 +618,7 @@ typedef uint64_t uintmax_t; #define textexit _Section(".text.exit") noinstrument #define textreal _Section(".text.real") #define textwindows _Section(".text.windows") -#define textsyscall _Section(".text.syscall") +#define textsyscall _Section(".text.syscall") noinline #define antiquity _Section(".text.antiquity") #else #define testonly diff --git a/libc/intrin/asan.c b/libc/intrin/asan.c index 3983a2a7a..e9c604c58 100644 --- a/libc/intrin/asan.c +++ b/libc/intrin/asan.c @@ -20,17 +20,19 @@ #include "libc/bits/bits.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/intrin/asan.internal.h" -#include "libc/linux/exit.h" -#include "libc/linux/write.h" #include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/hook/hook.h" +#include "libc/nt/enum/version.h" +#include "libc/nt/runtime.h" #include "libc/runtime/directmap.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/map.h" +#include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/prot.h" #include "third_party/dlmalloc/dlmalloc.internal.h" @@ -409,29 +411,40 @@ static const char *__asan_describe_access_poison(char *p) { } } +static textsyscall wontreturn void __asan_exit(int rc) { + if (!IsWindows()) { + asm volatile("syscall" + : /* no outputs */ + : "a"(__NR_exit_group), "D"(rc) + : "memory"); + unreachable; + } else { + ExitProcess(rc); + } +} + static textsyscall ssize_t __asan_write(const void *data, size_t size) { ssize_t rc; - if (weaken(write)) { - if ((rc = weaken(write)(2, data, size)) != -1) { - return rc; + uint32_t wrote; + if (!IsWindows()) { + asm volatile("syscall" + : "=a"(rc) + : "0"(__NR_write), "D"(2), "S"(data), "d"(size) + : "rcx", "r11", "memory"); + return rc; + } else { + if (WriteFile(GetStdHandle(kNtStdErrorHandle), data, size, &wrote, 0)) { + return wrote; + } else { + return -1; } } - return LinuxWrite(2, data, size); } static ssize_t __asan_write_string(const char *s) { return __asan_write(s, __asan_strlen(s)); } -static textsyscall wontreturn void __asan_exit(int rc) { - if (weaken(_Exit)) { - weaken(_Exit)(rc); - } else { - LinuxExit(rc); - } - for (;;) asm("hlt"); -} - static wontreturn void __asan_abort(void) { if (weaken(__die)) weaken(__die)(); if (weaken(abort)) weaken(abort)(); @@ -640,7 +653,9 @@ void __asan_stack_free(char *p, size_t size, int classid) { __asan_deallocate(p, kAsanStackFree); } -void __asan_handle_no_return_impl(uintptr_t rsp) { +void __asan_handle_no_return(void) { + uintptr_t rsp; + rsp = (uintptr_t)__builtin_frame_address(0); __asan_unpoison(rsp, ROUNDUP(rsp, STACKSIZE) - rsp); } @@ -660,11 +675,11 @@ void __asan_unregister_globals(struct AsanGlobal g[], int n) { } } -void __asan_report_load_impl(uint8_t *addr, int size) { +void __asan_report_load(uint8_t *addr, int size) { __asan_report_memory_fault(addr, size, "load"); } -void __asan_report_store_impl(uint8_t *addr, int size) { +void __asan_report_store(uint8_t *addr, int size) { __asan_report_memory_fault(addr, size, "store"); } @@ -790,6 +805,10 @@ textstartup void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) { static bool once; if (!cmpxchg(&once, false, true)) return; + if (IsWindows() && NtGetVersion() < kNtVersionWindows10) { + __asan_write_string("error: asan binaries require windows10\n"); + __asan_exit(0); /* So `make MODE=dbg test` passes w/ Windows7 */ + } REQUIRE(_mmi); REQUIRE(__mmap); REQUIRE(MAP_ANONYMOUS); diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index 4506a7ab8..d71f495e7 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -25,7 +25,9 @@ LIBC_INTRIN_A_CHECKS = \ LIBC_INTRIN_A_DIRECTDEPS = \ LIBC_STUBS \ - LIBC_NEXGEN32E + LIBC_SYSV \ + LIBC_NEXGEN32E \ + LIBC_NT_KERNEL32 LIBC_INTRIN_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_INTRIN_A_DIRECTDEPS),$($(x)))) diff --git a/libc/intrin/ntgetversion.c b/libc/intrin/ntgetversion.c new file mode 100644 index 000000000..60b4fae7a --- /dev/null +++ b/libc/intrin/ntgetversion.c @@ -0,0 +1,24 @@ +/*-*- 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 2021 Justine Alexandra Roberts Tunney │ +│ │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nt/struct/teb.h" +#include "libc/runtime/runtime.h" + +textwindows noasan int NtGetVersion(void) { + return (NtGetPeb()->OSMajorVersion & 0xff) << 8 | NtGetPeb()->OSMinorVersion; +} diff --git a/libc/intrin/somanyasan.S b/libc/intrin/somanyasan.S index 17df1d7d4..6678f2e2f 100644 --- a/libc/intrin/somanyasan.S +++ b/libc/intrin/somanyasan.S @@ -25,6 +25,18 @@ / since ASAN has the same stylistic hugeness as UBSAN. / We also guard all the functions, against reentrancy. + .rodata.cst4 +__asan_option_detect_stack_use_after_return: + .long 0 + .endobj __asan_option_detect_stack_use_after_return,globl + .previous + + .bss +__asan_noreentry: + .byte 0 + .endobj __asan_noreentry + .previous + __asan_report_load1: push $1 jmp OnReportLoad @@ -43,14 +55,18 @@ __asan_report_load8: .endfn __asan_report_load8,globl __asan_report_load16: push $16 -/ 𝑠𝑙𝑖𝑑𝑒 + jmp OnReportLoad .endfn __asan_report_load16,globl +__asan_report_load32: + push $32 +/ 𝑠𝑙𝑖𝑑𝑒 + .endfn __asan_report_load32,globl OnReportLoad: pop %rsi / 𝑠𝑙𝑖𝑑𝑒 .endfn OnReportLoad __asan_report_load_n: - lea __asan_report_load_impl(%rip),%r11 + lea __asan_report_load(%rip),%r11 jmp __asan_report_noreentry .endfn __asan_report_load_n,globl @@ -83,7 +99,7 @@ ReportStore: / 𝑠𝑙𝑖𝑑𝑒 .endfn ReportStore __asan_report_store_n: - lea __asan_report_store_impl(%rip),%r11 + lea __asan_report_store(%rip),%r11 / 𝑠𝑙𝑖𝑑𝑒 .endfn __asan_report_store_n,globl @@ -198,31 +214,6 @@ OnStackMalloc: jmp __asan_stack_malloc .endfn OnStackMalloc -__asan_handle_no_return: - push %rbp - mov %rsp,%rbp - lea 8(%rsp),%rdi - call __asan_handle_no_return_impl - pop %rbp - ret - .endfn __asan_handle_no_return,globl - -__asan_before_dynamic_init: - push %rbp - mov %rsp,%rbp - ud2 - pop %rbp - ret - .endfn __asan_before_dynamic_init,globl - -__asan_after_dynamic_init: - push %rbp - mov %rsp,%rbp - ud2 - pop %rbp - ret - .endfn __asan_after_dynamic_init,globl - __asan_version_mismatch_check_v8: ret .endfn __asan_version_mismatch_check_v8,globl @@ -240,14 +231,76 @@ __asan_version_mismatch_check_v8: pop %rdi .init.end 301,_init_asan - .rodata.cst4 -__asan_option_detect_stack_use_after_return: - .long 0 - .endobj __asan_option_detect_stack_use_after_return,globl - .previous +__asan_before_dynamic_init: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_before_dynamic_init,globl - .bss -__asan_noreentry: - .byte 0 - .endobj __asan_noreentry - .previous +__asan_after_dynamic_init: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_after_dynamic_init,globl + +__asan_load1: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_load1,globl +__asan_load2: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_load2,globl +__asan_load4: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_load4,globl +__asan_load8: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_load8,globl +__asan_load16: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_load16,globl +__asan_load32: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_load32,globl + +__asan_store1: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_store1,globl +__asan_store2: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_store2,globl +__asan_store4: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_store4,globl +__asan_store8: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_store8,globl +__asan_store16: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_store16,globl +__asan_store32: + push %rbp + mov %rsp,%rbp + ud2 + .endfn __asan_store32,globl diff --git a/libc/nt/struct/teb.h b/libc/nt/struct/teb.h index 972b9c5e3..38d870229 100644 --- a/libc/nt/struct/teb.h +++ b/libc/nt/struct/teb.h @@ -4,14 +4,15 @@ #include "libc/nt/struct/peb.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -/* These macros address directly into NT's TEB a.k.a. TIB */ -#define NtGetPeb() gs((struct NtPeb **)(0x60ULL)) -#define NtGetTeb() gs((void **)(0x30)) /* %gs:0 linear address */ -#define NtGetPid() gs((uint32_t *)(0x40)) /* GetCurrentProcessId() */ -#define NtGetTid() gs((uint32_t *)(0x48)) /* GetCurrentThreadId() */ -#define NtGetErr() gs((int *)(0x68)) -#define NtGetVersion() \ - ((NtGetPeb()->OSMajorVersion & 0xff) << 8 | NtGetPeb()->OSMinorVersion) +/* + * These macros address directly into NT's TEB a.k.a. TIB + * Any function that does this needs the `noasan` keyword + */ +#define NtGetPeb() gs((struct NtPeb **)(0x60ULL)) +#define NtGetTeb() gs((void **)(0x30)) /* %gs:0 linear address */ +#define NtGetPid() gs((uint32_t *)(0x40)) /* GetCurrentProcessId() */ +#define NtGetTid() gs((uint32_t *)(0x48)) /* GetCurrentThreadId() */ +#define NtGetErr() gs((int *)(0x68)) #define _NtGetSeh() gs((void **)(0x00)) #define _NtGetStackHigh() gs((void **)(0x08)) #define _NtGetStackLow() gs((void **)(0x10)) diff --git a/libc/rand/rand.mk b/libc/rand/rand.mk index 90f631f06..1fb297001 100644 --- a/libc/rand/rand.mk +++ b/libc/rand/rand.mk @@ -24,14 +24,14 @@ LIBC_RAND_A_CHECKS = \ $(LIBC_RAND_A_HDRS:%=o/$(MODE)/%.ok) LIBC_RAND_A_DIRECTDEPS = \ - LIBC_STUBS \ - LIBC_INTRIN \ - LIBC_TINYMATH \ + LIBC_CALLS \ LIBC_NEXGEN32E \ LIBC_NT_KERNEL32 \ LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ LIBC_SYSV_CALLS \ - LIBC_SYSV + LIBC_TINYMATH LIBC_RAND_A_DEPS := \ $(call uniq,$(foreach x,$(LIBC_RAND_A_DIRECTDEPS),$($(x)))) diff --git a/libc/rand/rand64.c b/libc/rand/rand64.c index daa2b409e..b3e1d163a 100644 --- a/libc/rand/rand64.c +++ b/libc/rand/rand64.c @@ -33,7 +33,7 @@ hidden extern uint64_t g_rando64; * * @see rngset() */ -nodebuginfo uint64_t(rand64)(void) { +nodebuginfo uint64_t rand64(void) { uint64_t res; if (X86_HAVE(RDRND)) { res = rdrand(); diff --git a/libc/rand/winrandish.c b/libc/rand/winrandish.c index 2beed4539..cd959e15a 100644 --- a/libc/rand/winrandish.c +++ b/libc/rand/winrandish.c @@ -16,11 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/nexgen32e/rdtsc.h" #include "libc/nt/dll.h" #include "libc/nt/events.h" #include "libc/nt/struct/point.h" -#include "libc/nt/struct/teb.h" #include "libc/rand/rand.h" /** @@ -29,7 +29,7 @@ textwindows int64_t winrandish(void) { int64_t res; struct NtPoint point; - res = ((int64_t)NtGetPid() << 17) ^ NtGetTid() ^ rdtsc(); + res = ((int64_t)getpid() << 17) ^ gettid() ^ rdtsc(); /* * This function is intended for older CPUs built before 2012, so * let's avoid having our CUI apps yoink USER32.DLL until we're diff --git a/libc/runtime/arememoryintervalsok.c b/libc/runtime/arememoryintervalsok.c index 47ec948ab..66943a1ff 100644 --- a/libc/runtime/arememoryintervalsok.c +++ b/libc/runtime/arememoryintervalsok.c @@ -19,7 +19,7 @@ #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" -bool AreMemoryIntervalsOk(const struct MemoryIntervals *mm) { +noasan bool AreMemoryIntervalsOk(const struct MemoryIntervals *mm) { int i; for (i = 0; i < mm->i; ++i) { if (mm->p[i].y < mm->p[i].x) return false; diff --git a/libc/runtime/assertfail.c b/libc/runtime/assertfail.c index 51e7f64be..215f1ece5 100644 --- a/libc/runtime/assertfail.c +++ b/libc/runtime/assertfail.c @@ -17,38 +17,83 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/bits/bits.h" #include "libc/bits/weaken.h" -#include "libc/calls/calls.h" +#include "libc/dce.h" #include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/alloca.h" -#include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/fileno.h" +#include "libc/nt/runtime.h" +#include "libc/sysv/consts/nr.h" + +static noasan size_t __assert_strlen(const char *s) { + size_t i = 0; + while (s[i]) ++i; + return i; +} + +static noasan char *__assert_stpcpy(char *d, const char *s) { + size_t i; + for (i = 0;; ++i) { + if (!(d[i] = s[i])) { + return d + i; + } + } +} + +static textsyscall noasan wontreturn void __assert_exit(int rc) { + if (!IsWindows()) { + asm volatile("syscall" + : /* no outputs */ + : "a"(__NR_exit_group), "D"(rc) + : "memory"); + unreachable; + } else { + ExitProcess(rc); + } +} + +static textsyscall noasan ssize_t __assert_write(const void *data, + size_t size) { + ssize_t rc; + uint32_t wrote; + if (!IsWindows()) { + asm volatile("syscall" + : "=a"(rc) + : "0"(__NR_write), "D"(2), "S"(data), "d"(size) + : "rcx", "r11", "memory"); + return rc; + } else { + if (WriteFile(GetStdHandle(kNtStdErrorHandle), data, size, &wrote, 0)) { + return wrote; + } else { + return -1; + } + } +} /** * Handles failure of assert() macro. */ -relegated void __assert_fail(const char *expr, const char *file, int line) { +relegated wontreturn noasan void __assert_fail(const char *expr, + const char *file, int line) { static bool once; char *msg, *p, linebuf[16]; unsigned i, exprlen, filelen; - if (!once) { - once = true; - exprlen = expr ? strlen(expr) : 0; - filelen = file ? strlen(file) : 0; + if (cmpxchg(&once, false, true)) { + exprlen = expr ? __assert_strlen(expr) : 0; + filelen = file ? __assert_strlen(file) : 0; p = msg = alloca(MIN(512, exprlen + filelen + 64)); - p = mempcpy(p, file, filelen); - p = stpcpy(p, ":"); + p = __assert_stpcpy(p, file); + p = __assert_stpcpy(p, ":"); if (line < 1) line = 1; for (i = 0; line; line /= 10) linebuf[i++] = '0' + line % 10; while (i) *p++ = linebuf[--i]; - p = stpcpy(p, ":"); - p = mempcpy(p, expr, exprlen); - p = stpcpy(p, "\r\n"); - write(STDERR_FILENO, msg, p - msg); + p = __assert_stpcpy(p, ":"); + p = __assert_stpcpy(p, expr); + p = __assert_stpcpy(p, "\r\n"); + __assert_write(msg, p - msg); if (weaken(__die)) weaken(__die)(); } - abort(); - unreachable; + __assert_exit(23); } diff --git a/libc/runtime/cxaatexit.c b/libc/runtime/cxaatexit.c index d68f9e5ba..444c611bd 100644 --- a/libc/runtime/cxaatexit.c +++ b/libc/runtime/cxaatexit.c @@ -51,7 +51,7 @@ static struct CxaAtexitBlocks { * @return 0 on success or nonzero w/ errno * @note folks have forked libc in past just to unbloat atexit() */ -int __cxa_atexit(void *fp, void *arg, void *pred) { +noasan int __cxa_atexit(void *fp, void *arg, void *pred) { unsigned i; struct CxaAtexitBlock *b, *b2; b = __cxa_blocks.p; @@ -83,7 +83,7 @@ int __cxa_atexit(void *fp, void *arg, void *pred) { * * @param pred can be null to match all */ -void __cxa_finalize(void *pred) { +noasan void __cxa_finalize(void *pred) { unsigned i; unsigned long mask; struct CxaAtexitBlock *b, *b2; diff --git a/libc/runtime/directmap.c b/libc/runtime/directmap.c index 9a4c8d6c1..c72769e18 100644 --- a/libc/runtime/directmap.c +++ b/libc/runtime/directmap.c @@ -28,8 +28,8 @@ * bypassed by calling this function. However the caller is responsible * for passing the magic memory handle on Windows NT to CloseHandle(). */ -struct DirectMap __mmap(void *addr, size_t size, unsigned prot, unsigned flags, - int fd, int64_t off) { +noasan struct DirectMap __mmap(void *addr, size_t size, unsigned prot, + unsigned flags, int fd, int64_t off) { if (!IsWindows()) { return (struct DirectMap){mmap$sysv(addr, size, prot, flags, fd, off), kNtInvalidHandleValue}; diff --git a/libc/runtime/directmapnt.c b/libc/runtime/directmapnt.c index fb540a153..19833004b 100644 --- a/libc/runtime/directmapnt.c +++ b/libc/runtime/directmapnt.c @@ -25,8 +25,9 @@ #include "libc/runtime/directmap.h" #include "libc/sysv/consts/prot.h" -textwindows struct DirectMap __mmap$nt(void *addr, size_t size, unsigned prot, - int64_t handle, int64_t off) { +textwindows noasan struct DirectMap __mmap$nt(void *addr, size_t size, + unsigned prot, int64_t handle, + int64_t off) { struct DirectMap dm; if ((dm.maphandle = CreateFileMappingNuma( handle, &kNtIsInheritable, diff --git a/libc/runtime/findmemoryinterval.c b/libc/runtime/findmemoryinterval.c index 9342e5e43..7e37d39f0 100644 --- a/libc/runtime/findmemoryinterval.c +++ b/libc/runtime/findmemoryinterval.c @@ -18,7 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/memtrack.h" -unsigned FindMemoryInterval(const struct MemoryIntervals *mm, int x) { +noasan unsigned FindMemoryInterval(const struct MemoryIntervals *mm, int x) { unsigned l, m, r; l = 0; r = mm->i; diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index e0be9f52d..4ecefb6b1 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -46,7 +46,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" -static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) { +static textwindows noasan char16_t *ParseInt(char16_t *p, int64_t *x) { *x = 0; while (*p == ' ') p++; while ('0' <= *p && *p <= '9') { @@ -56,8 +56,8 @@ static textwindows char16_t *ParseInt(char16_t *p, int64_t *x) { return p; } -static noinline textwindows void ForkIo(int64_t h, void *buf, size_t n, - bool32 (*f)()) { +static noinline textwindows noasan void ForkIo(int64_t h, void *buf, size_t n, + bool32 (*f)()) { char *p; size_t i; uint32_t x; @@ -66,15 +66,17 @@ static noinline textwindows void ForkIo(int64_t h, void *buf, size_t n, } } -static noinline textwindows void WriteAll(int64_t h, void *buf, size_t n) { +static noinline textwindows noasan void WriteAll(int64_t h, void *buf, + size_t n) { ForkIo(h, buf, n, WriteFile); } -static noinline textwindows void ReadAll(int64_t h, void *buf, size_t n) { +static textwindows noinline noasan void ReadAll(int64_t h, void *buf, + size_t n) { ForkIo(h, buf, n, ReadFile); } -textwindows void WinMainForked(void) { +textwindows noasan void WinMainForked(void) { void *addr; jmp_buf jb; uint64_t size; @@ -117,13 +119,13 @@ textwindows void WinMainForked(void) { textwindows int fork$nt(void) { jmp_buf jb; - int i, rc, pid; char exe[PATH_MAX]; int64_t reader, writer; + int i, rc, pid, releaseme; char *p, forkvar[6 + 21 + 1 + 21 + 1]; struct NtStartupInfo startinfo; struct NtProcessInformation procinfo; - if ((pid = __getemptyfd()) == -1) return -1; + if ((pid = releaseme = __reservefd()) == -1) return -1; if (!setjmp(jb)) { if (CreatePipe(&reader, &writer, &kNtIsInheritable, 0)) { p = stpcpy(forkvar, "_FORK="); @@ -148,6 +150,7 @@ textwindows int fork$nt(void) { g_fds.p[pid].kind = kFdProcess; g_fds.p[pid].handle = procinfo.hProcess; g_fds.p[pid].flags = O_CLOEXEC; + releaseme = -1; } WriteAll(writer, jb, sizeof(jb)); WriteAll(writer, &_mmi.i, sizeof(_mmi.i)); @@ -170,5 +173,8 @@ textwindows int fork$nt(void) { } else { rc = 0; } + if (releaseme != -1) { + __releasefd(releaseme); + } return rc; } diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index ea084bd15..3dcc50427 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -32,7 +32,7 @@ struct DosArgv { wint_t wc; }; -static textwindows wint_t DecodeDosArgv(const char16_t **s) { +static textwindows noasan wint_t DecodeDosArgv(const char16_t **s) { wint_t x, y; for (;;) { if (!(x = *(*s)++)) break; @@ -50,7 +50,7 @@ static textwindows wint_t DecodeDosArgv(const char16_t **s) { return x; } -static textwindows void AppendDosArgv(struct DosArgv *st, wint_t wc) { +static textwindows noasan void AppendDosArgv(struct DosArgv *st, wint_t wc) { uint64_t w; w = tpenc(wc); do { @@ -78,8 +78,8 @@ static textwindows void AppendDosArgv(struct DosArgv *st, wint_t wc) { * @see libc/runtime/ntspawn.c * @note kudos to Simon Tatham for figuring out quoting behavior */ -textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, - char **argv, size_t max) { +textwindows noasan int GetDosArgv(const char16_t *cmdline, char *buf, + size_t size, char **argv, size_t max) { bool inquote; size_t i, argc, slashes, quotes; struct DosArgv st; diff --git a/libc/runtime/getdosenviron.c b/libc/runtime/getdosenviron.c index 595648bb2..764f07429 100644 --- a/libc/runtime/getdosenviron.c +++ b/libc/runtime/getdosenviron.c @@ -18,6 +18,30 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/str/tpenc.h" +#include "libc/str/utf16.h" + +static textwindows noasan axdx_t Recode16to8(char *dst, size_t dstsize, + const char16_t *src) { + axdx_t r; + uint64_t w; + wint_t x, y; + for (r.ax = 0, r.dx = 0;;) { + if (!(x = src[r.dx++])) break; + if (IsUtf16Cont(x)) continue; + if (!IsUcs2(x)) { + if (!(y = src[r.dx++])) break; + x = MergeUtf16(x, y); + } + for (w = tpenc(x); w && r.ax + 1 < dstsize; w >>= 8) { + dst[r.ax++] = w & 0xFF; + } + } + if (r.ax < dstsize) { + dst[r.ax] = 0; + } + return r; +} /** * Transcodes NT environment variable block from UTF-16 to UTF-8. @@ -29,8 +53,8 @@ * @param max is the pointer count capacity of envp * @return number of variables decoded, excluding NULL-terminator */ -textwindows int GetDosEnviron(const char16_t *env, char *buf, size_t size, - char **envp, size_t max) { +textwindows noasan int GetDosEnviron(const char16_t *env, char *buf, + size_t size, char **envp, size_t max) { int i; axdx_t r; i = 0; @@ -38,7 +62,7 @@ textwindows int GetDosEnviron(const char16_t *env, char *buf, size_t size, --size; while (*env) { if (i + 1 < max) envp[i++] = buf; - r = tprecode16to8(buf, size, env); + r = Recode16to8(buf, size, env); size -= r.ax + 1; buf += r.ax + 1; env += r.dx; diff --git a/libc/runtime/memtrack.c b/libc/runtime/memtrack.c index d49eb276c..46a7924ab 100644 --- a/libc/runtime/memtrack.c +++ b/libc/runtime/memtrack.c @@ -24,7 +24,8 @@ #include "libc/str/str.h" #include "libc/sysv/errfuns.h" -static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) { +static noasan void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, + int n) { assert(i >= 0); assert(i + n <= mm->i); memcpy(mm->p + i, mm->p + i + n, @@ -32,7 +33,7 @@ static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) { mm->i -= n; } -static void CreateMemoryInterval(struct MemoryIntervals *mm, int i) { +static noasan void CreateMemoryInterval(struct MemoryIntervals *mm, int i) { assert(i >= 0); assert(i <= mm->i); assert(mm->i < ARRAYLEN(mm->p)); @@ -41,7 +42,7 @@ static void CreateMemoryInterval(struct MemoryIntervals *mm, int i) { ++mm->i; } -static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) { +static noasan int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) { if (mm->i == ARRAYLEN(mm->p)) return enomem(); CreateMemoryInterval(mm, i); mm->p[i].y = x - 1; @@ -49,8 +50,8 @@ static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) { return 0; } -int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, - void wincb(struct MemoryIntervals *, int, int)) { +noasan int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, + void wf(struct MemoryIntervals *, int, int)) { unsigned l, r; assert(y >= x); assert(AreMemoryIntervalsOk(mm)); @@ -81,16 +82,16 @@ int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y, --r; } if (l <= r) { - if (IsWindows() && wincb) { - wincb(mm, 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) { +noasan int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h, + int prot, int flags) { unsigned i; assert(y >= x); assert(AreMemoryIntervalsOk(mm)); diff --git a/libc/runtime/memtrack.h b/libc/runtime/memtrack.h index 45a93e843..6490e5e48 100644 --- a/libc/runtime/memtrack.h +++ b/libc/runtime/memtrack.h @@ -3,7 +3,6 @@ #include "libc/dce.h" #include "libc/macros.h" #include "libc/nt/enum/version.h" -#include "libc/nt/struct/teb.h" #include "libc/runtime/runtime.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/runtime/memtracknt.c b/libc/runtime/memtracknt.c index 2f09d536d..307bbd071 100644 --- a/libc/runtime/memtracknt.c +++ b/libc/runtime/memtracknt.c @@ -22,14 +22,14 @@ #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" -static void *GetFrameAddr(int f) { +static noasan void *GetFrameAddr(int f) { intptr_t a; a = f; a *= FRAMESIZE; return (void *)a; } -void ReleaseMemoryNt(struct MemoryIntervals *mm, int l, int r) { +noasan void ReleaseMemoryNt(struct MemoryIntervals *mm, int l, int r) { int i, ok; for (i = l; i <= r; ++i) { ok = UnmapViewOfFile(GetFrameAddr(mm->p[i].x)); diff --git a/libc/runtime/print.greg.c b/libc/runtime/print.greg.c index 8e18dda60..7b176dd89 100644 --- a/libc/runtime/print.greg.c +++ b/libc/runtime/print.greg.c @@ -35,7 +35,6 @@ static void __print$nt(const void *data, size_t len) { savexmm(xmm + 128); hand = __imp_GetStdHandle(kNtStdErrorHandle); __imp_WriteFile(hand, data, len, &wrote, NULL); - __imp_FlushFileBuffers(hand); loadxmm(xmm + 128); } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 015b02592..8fcc2c48f 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -72,6 +72,7 @@ void __print_string(const char *); void __fast_math(void); void *sbrk(intptr_t); int brk(void *); +int NtGetVersion(void); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § runtime » optimizations ─╬─│┼ diff --git a/libc/runtime/stackchkfail.c b/libc/runtime/stackchkfail.c index ff2c1da82..48f245a81 100644 --- a/libc/runtime/stackchkfail.c +++ b/libc/runtime/stackchkfail.c @@ -19,7 +19,6 @@ #include "libc/bits/pushpop.h" #include "libc/nt/enum/version.h" #include "libc/nt/runtime.h" -#include "libc/nt/struct/teb.h" #include "libc/runtime/internal.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/nr.h" @@ -29,7 +28,7 @@ /** * Aborts program under enemy fire to avoid being taken alive. */ -textsyscall void __stack_chk_fail(void) { +textsyscall noasan void __stack_chk_fail(void) { size_t len; const char *msg; int64_t ax, cx, si; diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 97e7a94ec..d0e2e8944 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -35,7 +35,6 @@ #include "libc/nt/pedef.internal.h" #include "libc/nt/process.h" #include "libc/nt/runtime.h" -#include "libc/nt/struct/teb.h" #include "libc/runtime/directmap.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.h" @@ -56,16 +55,16 @@ struct WinArgs { char envblock[ARG_MAX]; }; -static textwindows void SetTrueColor(void) { +static noasan textwindows void SetTrueColor(void) { SetEnvironmentVariable(u"TERM", u"xterm-truecolor"); } -static textwindows void MakeLongDoubleLongAgain(void) { +static noasan textwindows void MakeLongDoubleLongAgain(void) { int x87cw = 0x037f; /* let's hope win32 won't undo this */ asm volatile("fldcw\t%0" : /* no outputs */ : "m"(x87cw)); } -static textwindows void NormalizeCmdExe(void) { +static noasan textwindows void NormalizeCmdExe(void) { uint32_t mode; int64_t handle, hstdin, hstdout, hstderr; if ((int)weakaddr("v_ntsubsystem") == kNtImageSubsystemWindowsCui && @@ -96,7 +95,7 @@ static textwindows void NormalizeCmdExe(void) { } } -static textwindows wontreturn void WinMainNew(void) { +static noasan textwindows wontreturn void WinMainNew(void) { int64_t h; size_t size; int i, count; @@ -165,8 +164,8 @@ static textwindows wontreturn void WinMainNew(void) { * * @param hInstance call GetModuleHandle(NULL) from main if you need it */ -textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, - const char *lpCmdLine, int nCmdShow) { +noasan textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, + const char *lpCmdLine, int nCmdShow) { MakeLongDoubleLongAgain(); if (weaken(winsockinit)) weaken(winsockinit)(); if (weaken(WinMainForked)) weaken(WinMainForked)(); diff --git a/libc/sock/accept-nt.c b/libc/sock/accept-nt.c index faf33fb27..76aaaa332 100644 --- a/libc/sock/accept-nt.c +++ b/libc/sock/accept-nt.c @@ -34,12 +34,13 @@ textwindows int accept$nt(struct Fd *fd, void *addr, uint32_t *addrsize, uint32_t yes; for (;;) { if (!WSAPoll(&(struct pollfd$nt){fd->handle, POLLIN}, 1, 1000)) continue; - if ((client = __getemptyfd()) == -1) return -1; + if ((client = __reservefd()) == -1) return -1; if ((h = WSAAccept(fd->handle, addr, (int32_t *)addrsize, 0, 0)) != -1) { if (flags & SOCK_NONBLOCK) { yes = 1; if (__ioctlsocket$nt(g_fds.p[client].handle, FIONBIO, &yes) == -1) { __closesocket$nt(g_fds.p[client].handle); + __releasefd(client); return __winsockerr(); } } @@ -48,6 +49,7 @@ textwindows int accept$nt(struct Fd *fd, void *addr, uint32_t *addrsize, g_fds.p[client].handle = h; return client; } else { + __releasefd(client); return __winsockerr(); } } diff --git a/libc/sock/epoll.c b/libc/sock/epoll.c index 1964e90c0..63de0727e 100644 --- a/libc/sock/epoll.c +++ b/libc/sock/epoll.c @@ -1323,15 +1323,20 @@ static textwindows noinline int epoll_create1$nt(uint32_t flags) { int64_t ephnd; struct PortState *port_state; struct TsTreeNode *tree_node; - if ((fd = __getemptyfd()) == -1) return -1; if (wepoll_init() < 0) return -1; + if ((fd = __reservefd()) == -1) return -1; port_state = port_new(&ephnd); - if (!port_state) return -1; + if (!port_state) { + __releasefd(fd); + return -1; + } tree_node = port_state_to_handle_tree_node(port_state); if (ts_tree_add(&epoll__handle_tree, tree_node, (uintptr_t)ephnd) < 0) { /* This should never happen. */ port_delete(port_state); - RETURN_SET_ERROR(-1, kNtErrorAlreadyExists); + err_set_win_error(kNtErrorAlreadyExists); + __releasefd(fd); + return -1; } g_fds.p[fd].kind = kFdEpoll; g_fds.p[fd].handle = ephnd; diff --git a/libc/sock/kntwsadata.c b/libc/sock/kntwsadata.c index b7c9711ec..bf21ef8fc 100644 --- a/libc/sock/kntwsadata.c +++ b/libc/sock/kntwsadata.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" +#include "libc/nt/runtime.h" #include "libc/nt/winsock.h" #include "libc/runtime/runtime.h" #include "libc/sock/internal.h" @@ -32,15 +33,15 @@ */ hidden struct NtWsaData kNtWsaData; -textwindows static void winsockfini(void) { +static textwindows void winsockfini(void) { WSACleanup(); } -textwindows void winsockinit(void) { +textwindows noasan void winsockinit(void) { int rc; atexit(winsockfini); if ((rc = WSAStartup(VERSION, &kNtWsaData)) != 0 || kNtWsaData.wVersion != VERSION) { - abort(); + ExitProcess(123); } } diff --git a/libc/sock/socket-nt.c b/libc/sock/socket-nt.c index 82fc1e02e..31254eb40 100644 --- a/libc/sock/socket-nt.c +++ b/libc/sock/socket-nt.c @@ -29,7 +29,7 @@ textwindows int socket$nt(int family, int type, int protocol) { int fd; uint32_t yes; - if ((fd = __getemptyfd()) == -1) return -1; + if ((fd = __reservefd()) == -1) return -1; if ((g_fds.p[fd].handle = WSASocket(family, type & ~(CLOEXEC | NONBLOCK), protocol, NULL, 0, 0)) != -1) { if (type & NONBLOCK) { diff --git a/libc/stubs/asan.S b/libc/stubs/asan.S deleted file mode 100644 index 3cd9388d4..000000000 --- a/libc/stubs/asan.S +++ /dev/null @@ -1,388 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 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/macros.h" -.source __FILE__ - -/ @fileoverview Address Sanitizer Linker Poison - -__asan_addr_is_in_fake_stack: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_addr_is_in_fake_stack,weak - -__asan_after_dynamic_init: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_after_dynamic_init,weak - -__asan_alloca_poison: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_alloca_poison,weak - -__asan_allocas_unpoison: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_allocas_unpoison,weak - -__asan_before_dynamic_init: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_before_dynamic_init,weak - -__asan_get_current_fake_stack: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_get_current_fake_stack,weak - -__asan_handle_no_return: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_handle_no_return,weak - -__asan_init: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_init,weak - -__asan_load1: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_load1,weak - -__asan_load2: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_load2,weak - -__asan_load4: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_load4,weak - -__asan_load8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_load8,weak - -__asan_load16: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_load16,weak - -__asan_load32: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_load32,weak - -__asan_option_detect_stack_use_after_return: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_option_detect_stack_use_after_return,weak - -__asan_register_globals: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_register_globals,weak - -__asan_report_load1: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_load1,weak - -__asan_report_load2: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_load2,weak - -__asan_report_load4: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_load4,weak - -__asan_report_load8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_load8,weak - -__asan_report_load16: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_load16,weak - -__asan_report_load_n: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_load_n,weak - -__asan_report_store1: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store1,weak - -__asan_report_store2: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store2,weak - -__asan_report_store4: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store4,weak - -__asan_report_store8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store8,weak - -__asan_report_store16: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store16,weak - -__asan_report_store32: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store32,weak - -__asan_report_store_n: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_report_store_n,weak - -__asan_stack_free: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free,weak - -__asan_stack_free_0: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_0,weak - -__asan_stack_free_1: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_1,weak - -__asan_stack_free_10: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_10,weak - -__asan_stack_free_2: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_2,weak - -__asan_stack_free_3: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_3,weak - -__asan_stack_free_4: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_4,weak - -__asan_stack_free_5: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_5,weak - -__asan_stack_free_6: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_6,weak - -__asan_stack_free_7: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_7,weak - -__asan_stack_free_8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_8,weak - -__asan_stack_free_9: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_free_9,weak - -__asan_stack_malloc: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc,weak - -__asan_stack_malloc_0: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_0,weak - -__asan_stack_malloc_1: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_1,weak - -__asan_stack_malloc_2: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_2,weak - -__asan_stack_malloc_3: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_3,weak - -__asan_stack_malloc_4: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_4,weak - -__asan_stack_malloc_5: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_5,weak - -__asan_stack_malloc_6: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_6,weak - -__asan_stack_malloc_7: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_7,weak - -__asan_stack_malloc_8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_8,weak - -__asan_stack_malloc_9: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_9,weak - -__asan_stack_malloc_10: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_stack_malloc_10,weak - -__asan_store1: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_store1,weak - -__asan_store2: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_store2,weak - -__asan_store4: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_store4,weak - -__asan_store8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_store8,weak - -__asan_store16: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_store16,weak - -__asan_store32: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_store32,weak - -__asan_unregister_globals: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_unregister_globals,weak - -__asan_version_mismatch_check_v8: - push %rbp - mov %rsp,%rbp - ud2 - .endfn __asan_version_mismatch_check_v8,weak diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index b14dabdba..a42358c99 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -59,16 +59,31 @@ COSMOPOLITAN_C_START_ #define EXPECT_LE(C, X) _TEST2("EXPECT_LE", C, <=, (X), #C, " ≤ ", #X, 0) #define EXPECT_LT(C, X) _TEST2("EXPECT_LT", C, <, (X), #C, " < ", #X, 0) -#define _TEST2(NAME, WANT, OP, GOT, WANTCODE, OPSTR, GOTCODE, ISFATAL) \ - do { \ - autotype(WANT) Want = (WANT); \ - autotype(GOT) Got = (GOT); \ - if (!(Want OP Got)) { \ - testlib_showerror(FILIFU NAME, OPSTR, WANTCODE OPSTR GOTCODE, \ - testlib_formatint(Want), testlib_formatint(Got)); \ - testlib_onfail2(ISFATAL); \ - } \ - } while (0) +/** + * Enables setup and teardown of test directories. + * + * If the linker says this symbol exists then, regardless of its value, + * a unique test directory will be created at the start of each test, + * the test will be run with that directory as its working directory, + * and if the test succeeds it'll be removed along with any contents. + */ +extern char testlib_enable_tmp_setup_teardown; + +/** + * User-defined test setup function. + * + * The test runner will call this function before each TEST() if it's + * defined by the linkage. + */ +void SetUp(void); + +/** + * User-defined test cleanup function. + * + * The test runner will call this function after each TEST() if it's + * defined by the linkage. + */ +void TearDown(void); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § testing library » assert or die ─╬─│┼ @@ -87,39 +102,12 @@ COSMOPOLITAN_C_START_ __TEST_EQ(expect, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, GOT, \ __VA_ARGS__) -#define __TEST_EQ(KIND, FILE, LINE, FUNC, WANTCODE, GOTCODE, WANT, GOT, ...) \ - ({ \ - autotype(GOT) Got = _I(GOT); \ - typeof(Got) Want = _I(WANT); \ - testlib_ontest(); \ - if (Want != Got) { \ - TESTLIB_ONFAIL(FILE, FUNC); \ - TESTLIB_SHOWERROR(testlib_showerror_##KIND##_eq, LINE, WANTCODE, \ - GOTCODE, testlib_formatint(_I(Want)), \ - testlib_formatint(_I(Got)), "" __VA_ARGS__); \ - } \ - (void)0; \ - }) - #define ASSERT_NE(WANT, GOT, ...) \ __TEST_NE(assert, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, GOT, \ __VA_ARGS__) #define EXPECT_NE(WANT, GOT, ...) \ __TEST_NE(expect, __FILE__, __LINE__, __FUNCTION__, #WANT, #GOT, WANT, GOT, \ __VA_ARGS__) -#define __TEST_NE(KIND, FILE, LINE, FUNC, WANTCODE, GOTCODE, WANT, GOT, ...) \ - ({ \ - autotype(GOT) Got = (GOT); \ - typeof(Got) Want = (WANT); \ - testlib_ontest(); \ - if (Want == Got) { \ - TESTLIB_ONFAIL(FILE, FUNC); \ - TESTLIB_SHOWERROR(testlib_showerror_##KIND##_ne, LINE, WANTCODE, \ - GOTCODE, testlib_formatint(_I(Want)), \ - testlib_formatint(_I(Got)), "" __VA_ARGS__); \ - } \ - (void)0; \ - }) #define ASSERT_BETWEEN(BEG, END, GOT) \ assertBetween(FILIFU _I(BEG), _I(END), _I(GOT), \ @@ -245,6 +233,53 @@ COSMOPOLITAN_C_START_ const char *file; \ int line +#define TESTLIB_ONFAIL(FILE, FUNC) \ + if (g_testlib_shoulddebugbreak) DebugBreak(); \ + testlib_showerror_file = FILE; \ + testlib_showerror_func = FUNC + +#define TESTLIB_SHOWERROR(THUNK, ...) \ + (((typeof(&testlib_showerror_))strongaddr(#THUNK)))(__VA_ARGS__) + +#define __TEST_EQ(KIND, FILE, LINE, FUNC, WANTCODE, GOTCODE, WANT, GOT, ...) \ + ({ \ + autotype(GOT) Got = _I(GOT); \ + typeof(Got) Want = _I(WANT); \ + testlib_ontest(); \ + if (Want != Got) { \ + TESTLIB_ONFAIL(FILE, FUNC); \ + TESTLIB_SHOWERROR(testlib_showerror_##KIND##_eq, LINE, WANTCODE, \ + GOTCODE, testlib_formatint(_I(Want)), \ + testlib_formatint(_I(Got)), "" __VA_ARGS__); \ + } \ + (void)0; \ + }) + +#define __TEST_NE(KIND, FILE, LINE, FUNC, WANTCODE, GOTCODE, WANT, GOT, ...) \ + ({ \ + autotype(GOT) Got = (GOT); \ + typeof(Got) Want = (WANT); \ + testlib_ontest(); \ + if (Want == Got) { \ + TESTLIB_ONFAIL(FILE, FUNC); \ + TESTLIB_SHOWERROR(testlib_showerror_##KIND##_ne, LINE, WANTCODE, \ + GOTCODE, testlib_formatint(_I(Want)), \ + testlib_formatint(_I(Got)), "" __VA_ARGS__); \ + } \ + (void)0; \ + }) + +#define _TEST2(NAME, WANT, OP, GOT, WANTCODE, OPSTR, GOTCODE, ISFATAL) \ + do { \ + autotype(WANT) Want = (WANT); \ + autotype(GOT) Got = (GOT); \ + if (!(Want OP Got)) { \ + testlib_showerror(FILIFU NAME, OPSTR, WANTCODE OPSTR GOTCODE, \ + testlib_formatint(Want), testlib_formatint(Got)); \ + testlib_onfail2(ISFATAL); \ + } \ + } while (0) + typedef void (*testfn_t)(void); struct TestFixture { @@ -265,14 +300,6 @@ extern const testfn_t __testcase_start[], __testcase_end[]; extern const struct TestFixture __fixture_start[], __fixture_end[]; extern const struct TestFixture __combo_start[], __combo_end[]; -#define TESTLIB_ONFAIL(FILE, FUNC) \ - if (g_testlib_shoulddebugbreak) DebugBreak(); \ - testlib_showerror_file = FILE; \ - testlib_showerror_func = FUNC - -#define TESTLIB_SHOWERROR(THUNK, ...) \ - (((typeof(&testlib_showerror_))strongaddr(#THUNK)))(__VA_ARGS__) - void testlib_showerror_(int line, const char *wantcode, const char *gotcode, char *FREED_want, char *FREED_got, const char *fmt, ...) relegated; diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 7f7de41de..6a226016b 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -17,15 +17,17 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/weaken.h" +#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/errno.h" +#include "libc/fmt/fmt.h" +#include "libc/log/check.h" #include "libc/macros.h" #include "libc/nt/process.h" +#include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" - -void SetUp(void); -void TearDown(void); +#include "libc/x/x.h" void testlib_finish(void) { if (g_testlib_failed) { @@ -60,8 +62,18 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, * * @see ape/ape.lds */ + int x; + char cwd[PATH_MAX]; + char tmp[PATH_MAX]; const testfn_t *fn; - for (fn = start; fn != end; ++fn) { + for (x = 0, fn = start; fn != end; ++fn) { + if (weaken(testlib_enable_tmp_setup_teardown)) { + CHECK_NOTNULL(getcwd(cwd, sizeof(cwd))); + snprintf(tmp, sizeof(tmp), "o/tmp/%s.%d.%d", + program_invocation_short_name, getpid(), x++); + CHECK_NE(-1, makedirs(tmp, 0755), "tmp=%s", tmp); + CHECK_NE(-1, chdir(tmp), "tmp=%s", tmp); + } if (weaken(SetUp)) weaken(SetUp)(); errno = 0; SetLastError(0); @@ -71,5 +83,9 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, (*fn)(); getpid$sysv(); if (weaken(TearDown)) weaken(TearDown)(); + if (weaken(testlib_enable_tmp_setup_teardown)) { + CHECK_NE(-1, chdir(cwd)); + CHECK_NE(-1, rmrf(tmp)); + } } } diff --git a/test/libc/calls/commandv_test.c b/test/libc/calls/commandv_test.c index b25256767..329f8c6cc 100644 --- a/test/libc/calls/commandv_test.c +++ b/test/libc/calls/commandv_test.c @@ -34,12 +34,13 @@ #include "libc/x/x.h" uint64_t i; -char pathbuf[PATH_MAX], testdir[PATH_MAX], *oldpath; +char *oldpath; +char tmp[PATH_MAX]; +char pathbuf[PATH_MAX]; +char testlib_enable_tmp_setup_teardown; void SetUp(void) { - sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); - makedirs(testdir, 0755); - CHECK_NE(-1, chdir(testdir)); + static int x; mkdir("bin", 0755); mkdir("home", 0755); oldpath = strdup(nulltoempty(getenv("PATH"))); @@ -48,8 +49,6 @@ void SetUp(void) { void TearDown(void) { CHECK_NE(-1, setenv("PATH", oldpath, true)); - CHECK_NE(-1, chdir("../../..")); - CHECK_NE(-1, rmrf(testdir)); } TEST(commandv, testPathSearch) { diff --git a/test/libc/calls/fcntl_test.c b/test/libc/calls/fcntl_test.c index 11f00e127..22fa325da 100644 --- a/test/libc/calls/fcntl_test.c +++ b/test/libc/calls/fcntl_test.c @@ -26,20 +26,7 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char basedir[PATH_MAX]; -char testdir[PATH_MAX]; - -void SetUp(void) { - getcwd(basedir, ARRAYLEN(basedir)); - sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); - makedirs(testdir, 0755); - CHECK_NE(-1, chdir(testdir)); -} - -void TearDown(void) { - CHECK_NE(-1, chdir(basedir)); - CHECK_NE(-1, rmrf(testdir)); -} +char testlib_enable_tmp_setup_teardown; TEST(fcntl_getfl, testRemembersAccessMode) { int fd; diff --git a/test/libc/calls/getcwd_test.c b/test/libc/calls/getcwd_test.c index e7dcee02e..2d14c8410 100644 --- a/test/libc/calls/getcwd_test.c +++ b/test/libc/calls/getcwd_test.c @@ -26,20 +26,7 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char basedir[PATH_MAX]; -char testdir[PATH_MAX]; - -void SetUp(void) { - getcwd(basedir, ARRAYLEN(basedir)); - sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); - makedirs(testdir, 0755); - CHECK_NE(-1, chdir(testdir)); -} - -void TearDown(void) { - CHECK_NE(-1, chdir(basedir)); - CHECK_NE(-1, rmrf(testdir)); -} +char testlib_enable_tmp_setup_teardown; TEST(getcwd, test) { char buf[PATH_MAX]; diff --git a/test/libc/calls/lseek_test.c b/test/libc/calls/lseek_test.c index 905b75dda..2522649e0 100644 --- a/test/libc/calls/lseek_test.c +++ b/test/libc/calls/lseek_test.c @@ -25,18 +25,7 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testdir[PATH_MAX]; - -void SetUp(void) { - sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); - makedirs(testdir, 0755); - CHECK_NE(-1, chdir(testdir)); -} - -void TearDown(void) { - CHECK_NE(-1, chdir("../../..")); - CHECK_NE(-1, rmrf(testdir)); -} +char testlib_enable_tmp_setup_teardown; TEST(lseek, wat) { int fd, pid; diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index a1f3bbc23..997ac9742 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -25,18 +25,7 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" -char testdir[PATH_MAX]; - -void SetUp(void) { - sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); - makedirs(testdir, 0755); - CHECK_NE(-1, chdir(testdir)); -} - -void TearDown(void) { - CHECK_NE(-1, chdir("../../..")); - CHECK_NE(-1, rmrf(testdir)); -} +char testlib_enable_tmp_setup_teardown; TEST(mkdir, testNothingExists_ENOENT) { EXPECT_EQ(-1, mkdir("yo/yo/yo", 0755)); diff --git a/test/libc/runtime/mmap_test.c b/test/libc/runtime/mmap_test.c index 5977f46ea..6fd36482b 100644 --- a/test/libc/runtime/mmap_test.c +++ b/test/libc/runtime/mmap_test.c @@ -34,6 +34,8 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" +char testlib_enable_tmp_setup_teardown; + TEST(mmap, testMapFile) { int fd; char *p; @@ -107,10 +109,6 @@ TEST(mmap, customStackMemory_isAuthorized) { TEST(mmap, fileOffset) { int fd; char *map; - char testdir[PATH_MAX]; - sprintf(testdir, "o/tmp/%s.%d", program_invocation_short_name, getpid()); - ASSERT_NE(-1, makedirs(testdir, 0755)); - ASSERT_NE(-1, chdir(testdir)); ASSERT_NE(-1, (fd = open("foo", O_CREAT | O_RDWR, 0644))); EXPECT_NE(-1, ftruncate(fd, FRAMESIZE * 2)); EXPECT_NE(-1, pwrite(fd, "hello", 5, FRAMESIZE * 0)); @@ -120,8 +118,6 @@ TEST(mmap, fileOffset) { EXPECT_EQ(0, memcmp(map, "there", 5), "%#.*s", 5, map); EXPECT_NE(-1, munmap(map, FRAMESIZE)); EXPECT_NE(-1, close(fd)); - ASSERT_NE(-1, chdir("../../..")); - ASSERT_NE(-1, rmrf(testdir)); } TEST(isheap, nullPtr) { diff --git a/tool/viz/printpeb.c b/tool/viz/printpeb.c index 38cb67034..aee83de7a 100644 --- a/tool/viz/printpeb.c +++ b/tool/viz/printpeb.c @@ -80,7 +80,7 @@ const struct IdName kNtStartfFlagNames[] = { {0, 0}, }; -void PrintStartupInfo(void) { +noasan void PrintStartupInfo(void) { printf("\n\ ╔──────────────────────────────────────────────────────────────────────────────╗\n\ │ new technology § startup info │\n\ @@ -155,7 +155,7 @@ void PrintStdioInfo(void) { ft2str(GetFileType(g_fds.p[2].handle))); } -void PrintTeb(void) { +noasan void PrintTeb(void) { GetCurrentProcessId(); SetLastError(0x1234); printf("\n\