Make fixes and improvements

- Invent iso8601us() for faster timestamps
- Improve --strace descriptions of sigset_t
- Rebuild the Landlock Make bootstrap binary
- Introduce MODE=sysv for non-Windows builds
- Permit OFD fcntl() locks under pledge(flock)
- redbean can now protect your kernel from ddos
- Have vfork() fallback to sys_fork() not fork()
- Change kmalloc() to not die when out of memory
- Improve documentation for some termios functions
- Rewrite putenv() and friends to conform to POSIX
- Fix linenoise + strace verbosity issue on Windows
- Fix regressions in our ability to show backtraces
- Change redbean SetHeader() to no-op if value is nil
- Improve fcntl() so SQLite locks work in non-WAL mode
- Remove some unnecessary work during fork() on Windows
- Create redbean-based SSL reverse proxy for IPv4 TurfWar
- Fix ape/apeinstall.sh warning when using non-bash shells
- Add ProgramTrustedIp(), and IsTrustedIp() APIs to redbean
- Support $PWD, $UID, $GID, and $EUID in command interpreter
- Introduce experimental JTqFpD APE prefix for non-Windows builds
- Invent blackhole daemon for firewalling IP addresses via UNIX named socket
- Add ProgramTokenBucket(), AcquireToken(), and CountTokens() APIs to redbean
This commit is contained in:
Justine Tunney 2022-10-17 11:02:04 -07:00
parent 648bf6555c
commit f7ff77d865
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
209 changed files with 3818 additions and 998 deletions

View file

@ -25,6 +25,7 @@
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/_getenv.internal.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
@ -55,11 +56,6 @@
#define TOMBSTONE ((char *)-1)
#define READ24(s) READ32LE(s "\0")
struct Env {
char *s;
int i;
};
static char *p;
static char *q;
static char *r;
@ -71,7 +67,7 @@ static char *assign;
static char var[32];
static int lastchild;
static int exitstatus;
static char *envs[3000];
static char *envs[500];
static char *args[3000];
static const char *prog;
static char errbuf[512];
@ -148,27 +144,9 @@ static int GetSignalByName(const char *s) {
return 0;
}
static struct Env GetEnv(char **p, const char *k) {
int i, j;
for (i = 0; p[i]; ++i) {
for (j = 0;; ++j) {
if (!k[j] || k[j] == '=') {
if (p[i][j] == '=') {
return (struct Env){p[i] + j + 1, i};
}
break;
}
if (toupper(k[j] & 255) != toupper(p[i][j] & 255)) {
break;
}
}
}
return (struct Env){0, i};
}
static void PutEnv(char **p, const char *kv) {
struct Env e;
e = GetEnv(p, kv);
e = _getenv(p, kv);
p[e.i] = kv;
if (!e.s) p[e.i + 1] = 0;
}
@ -271,7 +249,7 @@ static int Read(void) {
static int Cd(void) {
const char *s;
if ((s = n > 1 ? args[1] : GetEnv(envs, "HOME").s)) {
if ((s = n > 1 ? args[1] : _getenv(envs, "HOME").s)) {
if (!chdir(s)) {
return 0;
} else {
@ -489,14 +467,27 @@ static const char *IntToStr(int x) {
}
static const char *GetVar(const char *key) {
static char vbuf[PATH_MAX];
if (key[0] == '$' && !key[1]) {
return IntToStr(getpid());
} else if (key[0] == '!' && !key[1]) {
return IntToStr(lastchild);
} else if (key[0] == '?' && !key[1]) {
return IntToStr(exitstatus);
} else if (!strcmp(key, "PWD")) {
_npassert(getcwd(vbuf, sizeof(vbuf)));
return vbuf;
} else if (!strcmp(key, "UID")) {
FormatInt32(vbuf, getuid());
return vbuf;
} else if (!strcmp(key, "GID")) {
FormatInt32(vbuf, getgid());
return vbuf;
} else if (!strcmp(key, "EUID")) {
FormatInt32(vbuf, geteuid());
return vbuf;
} else {
return GetEnv(envs, key).s;
return _getenv(envs, key).s;
}
}

View file

@ -19,8 +19,10 @@
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/morph.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/tls.h"
@ -30,7 +32,8 @@ extern int __threadcalls_start[];
#pragma weak __threadcalls_end
static privileged dontinline void FixupLockNops(void) {
__morph_begin();
sigset_t mask;
__morph_begin(&mask);
/*
* _NOPL("__threadcalls", func)
*
@ -54,7 +57,7 @@ static privileged dontinline void FixupLockNops(void) {
_base[*p + 1] = 0x67;
_base[*p + 2] = 0xe8;
}
__morph_end();
__morph_end(&mask);
}
void __enable_threads(void) {

View file

@ -19,6 +19,7 @@
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -32,6 +33,7 @@
#include "libc/nexgen32e/msr.h"
#include "libc/nt/thread.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/morph.h"
#include "libc/runtime/runtime.h"
#include "libc/stdalign.internal.h"
#include "libc/str/str.h"
@ -196,9 +198,10 @@ privileged void __enable_tls(void) {
if ((intptr_t)_tls_content && (IsWindows() || IsXnu())) {
int n;
uint64_t w;
sigset_t mask;
unsigned m, dis;
unsigned char *p;
__morph_begin();
__morph_begin(&mask);
if (IsXnu()) {
// Apple is quite straightforward to patch. We basically
@ -262,7 +265,7 @@ privileged void __enable_tls(void) {
}
}
__morph_end();
__morph_end(&mask);
}
// we are now allowed to use tls

View file

@ -224,9 +224,6 @@ textwindows void WinMainForked(void) {
AbortFork("CloseHandle");
}
// turn tls back on
__enable_tls();
// rewrap the stdin named pipe hack
// since the handles closed on fork
struct Fds *fds = VEIL("r", &g_fds);
@ -267,11 +264,14 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
uint32_t oldprot;
char **args, **args2;
char16_t pipename[64];
bool needtls, threaded;
int64_t reader, writer;
struct NtStartupInfo startinfo;
int i, n, pid, untrackpid, rc = -1;
char *p, forkvar[6 + 21 + 1 + 21 + 1];
struct NtProcessInformation procinfo;
threaded = __threaded;
needtls = __tls_enabled;
if (!setjmp(jb)) {
pid = untrackpid = __reservefd_unlocked(-1);
reader = CreateNamedPipe(CreatePipeName(pipename),
@ -345,9 +345,15 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
}
} else {
rc = 0;
if (needtls) {
__enable_tls();
}
if (threaded && !__threaded && _weaken(__enable_threads)) {
_weaken(__enable_threads)();
}
}
if (untrackpid != -1) {
__releasefd_unlocked(untrackpid);
__releasefd(untrackpid);
}
return rc;
}

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,14 +16,19 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/strace.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
/**
* Removes all environment variables.
*/
int clearenv(void) {
STRACE("clearenv() → 0");
environ = NULL;
return 0;
int sys_fork(void) {
axdx_t ad;
int ax, dx;
ad = __sys_fork();
ax = ad.ax;
dx = ad.dx;
if (IsXnu() && ax != -1) {
// eax always returned with childs pid
// edx is 0 for parent and 1 for child
ax &= dx - 1;
}
return ax;
}

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/blocksigs.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/calls/struct/sigset.internal.h"
@ -33,30 +34,15 @@
#include "libc/thread/tls.h"
int _fork(uint32_t dwCreationFlags) {
axdx_t ad;
bool threaded;
sigset_t old, all;
int ax, dx, parent;
sigfillset(&all);
_unassert(!sigprocmask(SIG_BLOCK, &all, &old));
BLOCK_SIGNALS;
if (__threaded && _weaken(_pthread_onfork_prepare)) {
_weaken(_pthread_onfork_prepare)();
}
if (!IsWindows()) {
ad = sys_fork();
ax = ad.ax;
dx = ad.dx;
if (IsXnu() && ax != -1) {
// eax always returned with childs pid
// edx is 0 for parent and 1 for child
ax &= dx - 1;
}
ax = sys_fork();
} else {
threaded = __threaded;
ax = sys_fork_nt(dwCreationFlags);
if (threaded && !__threaded && _weaken(__enable_threads)) {
_weaken(__enable_threads)();
}
}
if (!ax) {
if (!IsWindows()) {
@ -81,7 +67,7 @@ int _fork(uint32_t dwCreationFlags) {
}
STRACE("fork() → %d% m", ax);
}
_unassert(!sigprocmask(SIG_SETMASK, &old, 0));
ALLOW_SIGNALS;
return ax;
}
@ -92,6 +78,7 @@ int _fork(uint32_t dwCreationFlags) {
* @raise EAGAIN if `RLIMIT_NPROC` was exceeded or system lacked resources
* @raise ENOMEM if we require more vespene gas
* @asyncsignalsafe
* @threadsafe
*/
int fork(void) {
return _fork(0);

View file

@ -18,6 +18,7 @@
*/
#include "ape/sections.internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/runtime/morph.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/symbols.internal.h"
@ -45,13 +46,13 @@ privileged noinstrument noasan int __hook(void *ifunc,
size_t i;
char *p, *pe;
intptr_t addr;
sigset_t mask;
uint64_t code, mcode;
sigset_t mask, oldmask;
intptr_t kMcount = (intptr_t)&mcount;
intptr_t kProgramCodeStart = (intptr_t)_ereal;
intptr_t kPrivilegedStart = (intptr_t)__privileged_addr;
if (!symbols) return -1;
__morph_begin();
__morph_begin(&mask);
for (i = 0; i < symbols->count; ++i) {
if (symbols->addr_base + symbols->symbols[i].x < kProgramCodeStart) {
continue;
@ -112,6 +113,6 @@ privileged noinstrument noasan int __hook(void *ifunc,
}
}
}
__morph_end();
__morph_end(&mask);
return 0;
}

View file

@ -135,39 +135,39 @@ forceinline pureconst bool IsFixedFrame(int x) {
}
forceinline pureconst bool OverlapsImageSpace(const void *p, size_t n) {
const unsigned char *start, *ender;
const unsigned char *BegA, *EndA, *BegB, *EndB;
if (n) {
start = p;
ender = start + (n - 1);
return ((_base <= start && start < _end) ||
(_base <= ender && ender < _end) ||
(start < _base && _end <= ender));
BegA = p;
EndA = BegA + (n - 1);
BegB = _base;
EndB = _end - 1;
return MAX(BegA, BegB) < MIN(EndA, EndB);
} else {
return 0;
}
}
forceinline pureconst bool OverlapsArenaSpace(const void *p, size_t n) {
intptr_t x, y;
intptr_t BegA, EndA, BegB, EndB;
if (n) {
x = (intptr_t)p;
y = x + (n - 1);
return ((0x50000000 <= x && x <= 0x7ffdffff) ||
(0x50000000 <= y && y <= 0x7ffdffff) ||
(x < 0x50000000 && 0x7ffdffff < y));
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 x, y;
intptr_t BegA, EndA, BegB, EndB;
if (n) {
x = (intptr_t)p;
y = x + (n - 1);
return ((0x7fff0000 <= x && x <= 0x10007fffffff) ||
(0x7fff0000 <= y && y <= 0x10007fffffff) ||
(x < 0x7fff0000 && 0x10007fffffff < y));
BegA = (intptr_t)p;
EndA = BegA + (n - 1);
BegB = 0x7fff0000;
EndB = 0x10007fffffff;
return MAX(BegA, BegB) < MIN(EndA, EndB);
} else {
return 0;
}

View file

@ -18,6 +18,7 @@
*/
#define ShouldUseMsabiAttribute() 1
#include "ape/sections.internal.h"
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
@ -37,9 +38,6 @@
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
static int64_t vector;
static sigset_t oldss;
static privileged void __morph_mprotect(void *addr, size_t size, int prot,
int ntprot) {
bool cf;
@ -58,7 +56,7 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot,
_Exit(26);
}
#endif
if (ax) notpossible;
_npassert(!ax);
} else {
__imp_VirtualProtect(addr, size, ntprot, &op);
}
@ -69,29 +67,26 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot,
*
* @return 0 on success, or -1 w/ errno
*/
privileged void __morph_begin(void) {
privileged void __morph_begin(sigset_t *save) {
int ax;
bool cf;
intptr_t dx;
sigset_t ss = {{-1, -1}};
STRACE("__morph_begin()");
if (!IsWindows()) {
if (!IsOpenbsd()) {
asm volatile("mov\t$8,%%r10d\n\t"
"syscall"
: "=a"(ax), "=d"(dx)
: "0"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(&ss),
"1"(&oldss)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
if (ax) notpossible;
} else {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(-1u)
: "rcx", "r8", "r9", "r10", "r11", "memory");
oldss.__bits[0] = ax & 0xffffffff;
if (cf) notpossible;
}
if (IsOpenbsd()) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(-1u)
: "rcx", "r8", "r9", "r10", "r11", "memory");
save->__bits[0] = ax & 0xffffffff;
_npassert(!cf);
} else if (!IsWindows() && !IsMetal()) {
asm volatile("mov\t$8,%%r10d\n\t"
"syscall"
: "=a"(ax), "=d"(dx)
: "0"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(&ss), "1"(save)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
_npassert(!ax);
}
__morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_WRITE,
kNtPageWritecopy);
@ -100,29 +95,25 @@ privileged void __morph_begin(void) {
/**
* Begins code morphing executable.
*/
privileged void __morph_end(void) {
privileged void __morph_end(sigset_t *save) {
int ax;
long dx;
bool cf;
__morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_EXEC,
kNtPageExecuteRead);
if (!IsWindows()) {
if (!IsOpenbsd()) {
asm volatile("mov\t$8,%%r10d\n\t"
"syscall"
: "=a"(ax), "=d"(dx)
: "0"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(&oldss),
"1"(0)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
if (ax) notpossible;
} else {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_sigprocmask), "D"(SIG_SETMASK),
"S"(oldss.__bits[0])
: "rcx", "r8", "r9", "r10", "r11", "memory");
if (cf) notpossible;
}
if (IsOpenbsd()) {
asm volatile(CFLAG_ASM("syscall")
: CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx)
: "1"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(save->__bits[0])
: "rcx", "r8", "r9", "r10", "r11", "memory");
_npassert(!cf);
} else if (!IsWindows() && !IsMetal()) {
asm volatile("mov\t$8,%%r10d\n\t"
"syscall"
: "=a"(ax), "=d"(dx)
: "0"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(save), "1"(0)
: "rcx", "r8", "r9", "r10", "r11", "memory", "cc");
_npassert(!ax);
}
STRACE("__morph_end()");
}

12
libc/runtime/morph.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef COSMOPOLITAN_LIBC_RUNTIME_MORPH_H_
#define COSMOPOLITAN_LIBC_RUNTIME_MORPH_H_
#include "libc/calls/struct/sigset.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void __morph_begin(sigset_t *);
void __morph_end(sigset_t *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_RUNTIME_MORPH_H_ */

View file

@ -57,7 +57,7 @@ int atfork(void *, void *) libcesque;
int atexit(void (*)(void)) libcesque;
char *getenv(const char *) nosideeffect libcesque;
int putenv(char *) paramsnonnull();
int setenv(const char *, const char *, int) paramsnonnull();
int setenv(const char *, const char *, int);
int unsetenv(const char *);
int clearenv(void);
void fpreset(void);
@ -100,8 +100,6 @@ char *GetInterpreterExecutableName(char *, size_t);
void __printargs(const char *);
void __paginate(int, const char *);
int __arg_max(void);
void __morph_begin(void);
void __morph_end(void);
void __print_maps(void);
void __warn_if_powersave(void);
const char *__describe_os(void);

View file

@ -16,6 +16,8 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/log/libfatal.internal.h"
@ -29,7 +31,7 @@ textstartup int __strace_init(int argc, char **argv, char **envp, long *auxv) {
/* asan isn't initialized yet at runlevel 300 */
if (__intercept_flag(&argc, argv, "--strace") ||
__atoul(nulltoempty(__getenv(envp, "STRACE")))) {
++__strace;
atomic_store_explicit(&__strace, 1, memory_order_relaxed);
}
return (__argc = argc);
}

View file

@ -20,7 +20,6 @@
#include "libc/intrin/strace.internal.h"
#include "libc/thread/tls.h"
#include "libc/macros.internal.h"
.privileged
// Forks process without copying page tables.
//
@ -32,37 +31,55 @@
// do anything in a vfork()'d child process. TLS memory must not
// be disabled (it's enabled by default) since vfork() needs it.
//
// What makes vfork() dangerous is that any changes to memory in
// the child process can happen in the parent too. The exception
// to this rule is `errno` which is saved/restored in a register
// by this implementation. However, despite its dangers, vfork's
// performance is irresistible and wonderous to behold. If safer
// code is desired, consider posix_spawn() which uses vfork().
//
// Do not make the assumption that the parent is suspended until
// the child terminates since this impl calls fork() on Windows,
// OpenBSD, and MacOS.
// the child terminates since this uses the raw fork system call
// on Windows, OpenBSD, and MacOS. In that case the process will
// proceed without blocking the parent; however, the `__vforked`
// variable is still set to true in the child, so lock functions
// won't do anything, and other functions shall change behavior.
// This ensures that, even if the operating system does not give
// us the performance of vfork(), we'll still be able to cut out
// the libc overhead, e.g. pthread_atfork().
//
// @return pid of child process or 0 if forked process
// @returnstwice
// @threadsafe
// @vforksafe
vfork: call __require_tls
xor %edi,%edi # dwCreationFlags
#ifdef __SANITIZE_ADDRESS__
jmp fork # TODO: asan and vfork don't mix?
.endfn vfork,globl
#else
#if SupportsWindows()
testb IsWindows()
jnz sys_fork_nt # and we're lucky to have that
#endif
#if SupportsXnu()
testb IsXnu()
jnz fork
#endif
#if SupportsOpenbsd()
testb IsOpenbsd()
jnz fork # fake vfork plus msyscall issues
#endif
vfork:
#if !IsTiny()
push %rbp
mov %rsp,%rbp
.profilable
call __require_tls
#ifdef SYSDEBUG
ezlea .Llog,di
call __stracef
#endif /* SYSDEBUG */
#endif
pop %rbp
#endif
mov %fs:0,%r9 # get thread information block
#if SupportsWindows()
testb IsWindows()
jnz 6f # and we're lucky to have that
#endif
#ifdef __SANITIZE_ADDRESS__
jmp 5f # TODO: asan and vfork don't mix?
#endif
#if SupportsXnu()
testb IsXnu()
jnz 5f
#endif
#if SupportsOpenbsd()
testb IsOpenbsd()
jnz 5f # fake vfork plus msyscall issues
#endif
mov 0x3c(%r9),%r8d # avoid question of @vforksafe errno
pop %rsi # saves return address in a register
mov __NR_vfork(%rip),%eax
@ -79,12 +96,35 @@ vfork: call __require_tls
cmp $-4095,%eax
jae systemfive_error
mov %r8d,0x3c(%r9) # restore errno
test %eax,%eax
jnz .Lprnt
.Lchld: orb $TIB_FLAG_VFORKED,0x40(%r9)
1: test %eax,%eax
jnz .Lpar
.Lchi: orb $TIB_FLAG_VFORKED,0x40(%r9)
ret
.Lprnt: andb $~TIB_FLAG_VFORKED,0x40(%r9)
.Lpar: andb $~TIB_FLAG_VFORKED,0x40(%r9)
ret
#if SupportsXnu() || SupportsOpenbsd() || defined(__SANITIZE_ADDRESS__)
5: push %rbp
mov %rsp,%rbp
push %r9
push %r9
call sys_fork
pop %r9
pop %r9
pop %rbp
jmp 1b
#endif
#if SupportsWindows()
6: push %rbp
mov %rsp,%rbp
push %r9
push %r9
xor %edi,%edi # dwCreationFlags
call sys_fork_nt
pop %r9
pop %r9
pop %rbp
jmp 1b
#endif
.endfn vfork,globl
#ifdef SYSDEBUG
@ -93,5 +133,3 @@ vfork: call __require_tls
.asciz "vfork()\n"
.previous
#endif /* DEBUGSYS */
#endif /* __SANITIZE_ADDRESS__ */