mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Overhaul process spawning
This commit is contained in:
parent
99dc1281f5
commit
26e254fb4d
96 changed files with 1848 additions and 1541 deletions
Binary file not shown.
|
@ -91,7 +91,7 @@ VM = o/third_party/qemu/qemu-aarch64
|
|||
HOSTS ?= pi silicon
|
||||
else
|
||||
ARCH = x86_64
|
||||
HOSTS ?= freebsd openbsd netbsd rhel7 rhel5 xnu win10
|
||||
HOSTS ?= freebsd rhel7 rhel5 xnu win10 openbsd netbsd
|
||||
endif
|
||||
|
||||
ifeq ($(PREFIX),)
|
||||
|
@ -396,32 +396,6 @@ OBJECTIFY.c99.c = $(CC) $(OBJECTIFY.c.flags) -std=c99 -Wextra -Werror -pedantic-
|
|||
OBJECTIFY.c11.c = $(CC) $(OBJECTIFY.c.flags) -std=c11 -Wextra -Werror -pedantic-errors -c
|
||||
OBJECTIFY.c2x.c = $(CC) $(OBJECTIFY.c.flags) -std=c2x -Wextra -Werror -pedantic-errors -c
|
||||
|
||||
OBJECTIFY.real.c = \
|
||||
$(GCC) \
|
||||
-x-no-pg \
|
||||
$(OBJECTIFY.c.flags) \
|
||||
-wrapper build/realify.sh \
|
||||
-D__REAL_MODE__ \
|
||||
-ffixed-r8 \
|
||||
-ffixed-r9 \
|
||||
-ffixed-r10 \
|
||||
-ffixed-r11 \
|
||||
-ffixed-r12 \
|
||||
-ffixed-r13 \
|
||||
-ffixed-r14 \
|
||||
-ffixed-r15 \
|
||||
-mno-red-zone \
|
||||
-fcall-used-rbx \
|
||||
-fno-jump-tables \
|
||||
-fno-shrink-wrap \
|
||||
-fno-schedule-insns2 \
|
||||
-flive-range-shrinkage \
|
||||
-fno-omit-frame-pointer \
|
||||
-momit-leaf-frame-pointer \
|
||||
-mpreferred-stack-boundary=3 \
|
||||
-fno-delete-null-pointer-checks \
|
||||
-c
|
||||
|
||||
OBJECTIFY.ncabi.c = \
|
||||
$(GCC) \
|
||||
$(OBJECTIFY.c.flags) \
|
||||
|
|
|
@ -34,7 +34,6 @@ o/$(MODE)/%.o: %.cc ; @$(COMPILE) -AOBJECTIFY.cxx $(OBJECTIFY.cxx
|
|||
o/$(MODE)/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.initabi.o: %.initabi.c ; @$(COMPILE) -AOBJECTIFY.init $(OBJECTIFY.initabi.c) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.ncabi.o: %.ncabi.c ; @$(COMPILE) -AOBJECTIFY.nc $(OBJECTIFY.ncabi.c) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.real.o: %.c ; @$(COMPILE) -AOBJECTIFY.real $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
ifneq ($(ARCH), aarch64)
|
||||
o/%.o: %.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $<
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
#endif
|
||||
#include "libc/calls/calls.h"
|
||||
|
||||
// clears the teletypewriter display with empty cells
|
||||
// clears teletypewriter display
|
||||
//
|
||||
// - \e[H moves to top left of display
|
||||
// - \e[J erases whole display forward
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
write(1, "\e[H", 3);
|
||||
write(1, "\e[H\e[J", 6);
|
||||
}
|
||||
|
|
|
@ -7,17 +7,52 @@
|
|||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/posix_spawn.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int ws;
|
||||
char *line;
|
||||
while ((line = linenoiseWithHistory("IN> ", "foo"))) {
|
||||
fputs("OUT> ", stdout);
|
||||
fputs(line, stdout);
|
||||
fputs("\n", stdout);
|
||||
char ps1[100];
|
||||
sigset_t mask, om;
|
||||
posix_spawnattr_t attr;
|
||||
ShowCrashReports();
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGQUIT);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &mask, &om);
|
||||
posix_spawnattr_init(&attr);
|
||||
posix_spawnattr_setsigmask(&attr, &om);
|
||||
for (ws = 0;;) {
|
||||
if (WIFSIGNALED(ws)) {
|
||||
ksnprintf(ps1, sizeof(ps1), "\e[1;31m%G\e[0m :> ", ws, WTERMSIG(ws));
|
||||
} else {
|
||||
ksnprintf(ps1, sizeof(ps1), "%d :> ", ws, WEXITSTATUS(ws));
|
||||
}
|
||||
if (!(line = linenoiseWithHistory(ps1, "unbourne"))) {
|
||||
break;
|
||||
}
|
||||
if (*line) {
|
||||
int i = 0;
|
||||
char *args[64];
|
||||
args[i++] = strtok(line, " \t\v\r\n");
|
||||
while ((args[i++] = strtok(0, " \t\v\r\n"))) {
|
||||
}
|
||||
int pid;
|
||||
posix_spawnp(&pid, args[0], 0, &attr, args, environ);
|
||||
wait(&ws);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
posix_spawnattr_destroy(&attr);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/context.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
|
@ -225,13 +227,20 @@ textwindows bool __sig_handle(int sigops, int sig, int sic, ucontext_t *ctx) {
|
|||
uint32_t cmode;
|
||||
intptr_t hStderr;
|
||||
const char *signame;
|
||||
char *end, sigbuf[21], output[22];
|
||||
char *end, sigbuf[21], output[123];
|
||||
signame = strsignal_r(sig, sigbuf);
|
||||
STRACE("terminating due to uncaught %s", signame);
|
||||
if (__sig_is_core(sig)) {
|
||||
hStderr = GetStdHandle(kNtStdErrorHandle);
|
||||
if (GetConsoleMode(hStderr, &cmode)) {
|
||||
end = stpcpy(stpcpy(output, signame), "\n");
|
||||
end = stpcpy(output, signame);
|
||||
end = stpcpy(end, " ");
|
||||
end = stpcpy(
|
||||
end,
|
||||
DescribeBacktrace(
|
||||
ctx ? (struct StackFrame *)ctx->uc_mcontext.BP
|
||||
: (struct StackFrame *)__builtin_frame_address(0)));
|
||||
end = stpcpy(end, "\n");
|
||||
WriteFile(hStderr, output, end - output, 0, 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
/**
|
||||
|
@ -27,6 +29,8 @@
|
|||
void __assert_fail(const char *expr, const char *file, int line) {
|
||||
char ibuf[12];
|
||||
FormatInt32(ibuf, line);
|
||||
tinyprint(2, "\n", file, ":", ibuf, ": assert(", expr, ") failed\n", NULL);
|
||||
tinyprint(2, "\n", file, ":", ibuf, ": assert(", expr, ") failed (",
|
||||
program_invocation_short_name, " ",
|
||||
DescribeBacktrace(__builtin_frame_address(0)), ")\n", NULL);
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
|
|
@ -16,22 +16,28 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#define ShouldUseMsabiAttribute() 1
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/nt/accounting.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/startf.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/enum/threadaccess.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
|
@ -50,26 +56,31 @@
|
|||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define keywords textwindows dontasan dontubsan dontinstrument
|
||||
|
||||
extern long __klog_handle;
|
||||
|
||||
// clang-format off
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(ExitProcess) *const __imp_ExitProcess;
|
||||
__msabi extern typeof(GenerateConsoleCtrlEvent) *const __imp_GenerateConsoleCtrlEvent;
|
||||
__msabi extern typeof(GetCurrentThreadId) *const __imp_GetCurrentThreadId;
|
||||
__msabi extern typeof(GetExitCodeProcess) *const __imp_GetExitCodeProcess;
|
||||
__msabi extern typeof(GetLastError) *const __imp_GetLastError;
|
||||
__msabi extern typeof(OpenThread) *const __imp_OpenThread;
|
||||
__msabi extern typeof(SetConsoleCtrlHandler) *const __imp_SetConsoleCtrlHandler;
|
||||
__msabi extern typeof(TerminateThread) *const __imp_TerminateThread;
|
||||
__msabi extern typeof(UnmapViewOfFile) *const __imp_UnmapViewOfFile;
|
||||
__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject;
|
||||
// clang-format on
|
||||
|
||||
extern long __klog_handle;
|
||||
static void sys_execve_nt_relay(intptr_t, long, long, long);
|
||||
wontreturn void __switch_stacks(intptr_t, long, long, long,
|
||||
void (*)(intptr_t, intptr_t, long, long),
|
||||
intptr_t);
|
||||
|
||||
__msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
|
||||
return true; // block sigint and sigquit in execve() parent process
|
||||
}
|
||||
|
||||
static keywords void PurgeHandle(intptr_t h) {
|
||||
if (!h) return;
|
||||
if (h == -1) return;
|
||||
|
@ -83,66 +94,87 @@ static keywords void PurgeThread(intptr_t h) {
|
|||
}
|
||||
}
|
||||
|
||||
// this function runs on the original tiny stack that windows gave us.
|
||||
// we need to keep the original process alive simply to pass an int32.
|
||||
// so we unmap all memory to avoid getting a double whammy after fork.
|
||||
static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
|
||||
uint32_t i, dwExitCode;
|
||||
__imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1);
|
||||
PurgeThread(g_fds.stdin.thread);
|
||||
PurgeHandle(g_fds.stdin.reader);
|
||||
PurgeHandle(g_fds.stdin.writer);
|
||||
PurgeHandle(g_fds.p[0].handle);
|
||||
PurgeHandle(g_fds.p[1].handle);
|
||||
PurgeHandle(g_fds.p[2].handle);
|
||||
for (i = 0; i < _mmi.i; ++i) {
|
||||
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
|
||||
PurgeHandle(_mmi.p[i].h);
|
||||
static keywords void sys_execve_killer(void) {
|
||||
struct Dll *e;
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
for (e = dll_first(_pthread_list); e; e = dll_next(_pthread_list, e)) {
|
||||
enum PosixThreadStatus status;
|
||||
struct PosixThread *pt = POSIXTHREAD_CONTAINER(e);
|
||||
int tid = atomic_load_explicit(&pt->tib->tib_tid, memory_order_acquire);
|
||||
if (tid <= 0 || tid == __imp_GetCurrentThreadId()) continue;
|
||||
status = atomic_load_explicit(&pt->status, memory_order_acquire);
|
||||
if (status >= kPosixThreadTerminated) continue;
|
||||
int64_t hand;
|
||||
if ((hand = __imp_OpenThread(kNtThreadTerminate, false, tid))) {
|
||||
__imp_TerminateThread(hand, SIGKILL);
|
||||
__imp_CloseHandle(hand);
|
||||
}
|
||||
}
|
||||
do {
|
||||
__imp_WaitForSingleObject(h, -1);
|
||||
dwExitCode = kNtStillActive;
|
||||
__imp_GetExitCodeProcess(h, &dwExitCode);
|
||||
} while (dwExitCode == kNtStillActive);
|
||||
__imp_ExitProcess(dwExitCode);
|
||||
__builtin_unreachable();
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
}
|
||||
|
||||
keywords int sys_execve_nt(const char *program, char *const argv[],
|
||||
char *const envp[]) {
|
||||
int rc;
|
||||
size_t i;
|
||||
char progbuf[PATH_MAX];
|
||||
struct NtStartupInfo startinfo;
|
||||
struct NtProcessInformation procinfo;
|
||||
|
||||
if (strlen(program) + 4 + 1 > PATH_MAX) {
|
||||
return enametoolong();
|
||||
}
|
||||
|
||||
// this is a non-recoverable operation, so do some manual validation
|
||||
if (sys_faccessat_nt(AT_FDCWD, program, X_OK, 0) == -1) {
|
||||
stpcpy(stpcpy(progbuf, program), ".com");
|
||||
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
|
||||
program = progbuf;
|
||||
} else {
|
||||
stpcpy(stpcpy(progbuf, program), ".exe");
|
||||
// validate api usage
|
||||
if (strlen(program) + 4 < PATH_MAX) {
|
||||
char progbuf[PATH_MAX];
|
||||
char *end = stpcpy(progbuf, program);
|
||||
char suffixes[][5] = {"", ".com", ".exe"};
|
||||
for (i = 0; i < ARRAYLEN(suffixes); ++i) {
|
||||
stpcpy(end, suffixes[i]);
|
||||
if (sys_faccessat_nt(AT_FDCWD, progbuf, X_OK, 0) != -1) {
|
||||
program = progbuf;
|
||||
break;
|
||||
} else if (__imp_GetLastError() == kNtErrorSharingViolation) {
|
||||
return etxtbsy(); // TODO(jart): does this work
|
||||
} else {
|
||||
return eacces();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return enametoolong();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// execve operation is unrecoverable from this point
|
||||
//
|
||||
// POINT OF NO RETURN
|
||||
//
|
||||
//
|
||||
// NO! MNO!
|
||||
// MNO!! [NBK] MNNOO!
|
||||
// MMNO! MNNOO!!
|
||||
// MNOONNOO! MMMMMMMMMMPPPOII! MNNO!!!!
|
||||
// !O! NNO! MMMMMMMMMMMMMPPPOOOII!! NO!
|
||||
// ! MMMMMMMMMMMMMPPPPOOOOIII! !
|
||||
// MMMMMMMMMMMMPPPPPOOOOOOII!!
|
||||
// MMMMMOOOOOOPPPPPPPPOOOOMII!
|
||||
// MMMMM.. OPPMMP .,OMI!
|
||||
// MMMM:: o.,OPMP,.o ::I!!
|
||||
// NNM:::.,,OOPM!P,.::::!!
|
||||
// MMNNNNNOOOOPMO!!IIPPO!!O!
|
||||
// MMMMMNNNNOO:!!:!!IPPPPOO!
|
||||
// MMMMMNNOOMMNNIIIPPPOO!!
|
||||
// MMMONNMMNNNIIIOO!
|
||||
// MN MOMMMNNNIIIIIO! OO
|
||||
// MNO! IiiiiiiiiiiiI OOOO
|
||||
// NNN.MNO! O!!!!!!!!!O OONO NO!
|
||||
// MNNNNNO! OOOOOOOOOOO MMNNON!
|
||||
// MNNNNO! PPPPPPPPP MMNON!
|
||||
// OO! ON!
|
||||
//
|
||||
//
|
||||
|
||||
if (_weaken(pthread_kill_siblings_np)) {
|
||||
_weaken(pthread_kill_siblings_np)();
|
||||
// kill siblings
|
||||
sys_execve_killer();
|
||||
|
||||
// close win32 handles for memory mappings
|
||||
// unlike fork calling execve destroys all memory
|
||||
// closing a map handle won't impact the mapping itself
|
||||
for (i = 0; i < _mmi.i; ++i) {
|
||||
PurgeHandle(_mmi.p[i].h);
|
||||
}
|
||||
|
||||
// close non-stdio and cloexec handles
|
||||
// close o_cloexec fds and anything that isn't stdio
|
||||
for (i = 0; i < g_fds.n; ++i) {
|
||||
if (g_fds.p[i].kind == kFdEmpty) {
|
||||
g_fds.p[i].handle = -1;
|
||||
|
@ -152,12 +184,7 @@ keywords int sys_execve_nt(const char *program, char *const argv[],
|
|||
}
|
||||
}
|
||||
|
||||
if (_weaken(__klog_handle) && //
|
||||
*_weaken(__klog_handle) != 0 && //
|
||||
*_weaken(__klog_handle) != -1) {
|
||||
PurgeHandle(*_weaken(__klog_handle));
|
||||
}
|
||||
|
||||
// pass bitmask telling child which fds are sockets
|
||||
int bits;
|
||||
char buf[32], *v = 0;
|
||||
if (_weaken(socket)) {
|
||||
|
@ -169,23 +196,79 @@ keywords int sys_execve_nt(const char *program, char *const argv[],
|
|||
FormatInt32(stpcpy(buf, "__STDIO_SOCKETS="), bits);
|
||||
v = buf;
|
||||
}
|
||||
bzero(&startinfo, sizeof(startinfo));
|
||||
startinfo.cb = sizeof(struct NtStartupInfo);
|
||||
startinfo.dwFlags = kNtStartfUsestdhandles;
|
||||
startinfo.hStdInput = g_fds.p[0].handle;
|
||||
startinfo.hStdOutput = g_fds.p[1].handle;
|
||||
startinfo.hStdError = g_fds.p[2].handle;
|
||||
|
||||
// spawn the process
|
||||
rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &startinfo, &procinfo);
|
||||
// define stdio handles for the spawned subprocess
|
||||
struct NtStartupInfo si = {
|
||||
.cb = sizeof(struct NtStartupInfo),
|
||||
.dwFlags = kNtStartfUsestdhandles,
|
||||
.hStdInput = g_fds.p[0].handle,
|
||||
.hStdOutput = g_fds.p[1].handle,
|
||||
.hStdError = g_fds.p[2].handle,
|
||||
};
|
||||
|
||||
// launch the process
|
||||
struct NtProcessInformation pi;
|
||||
int rc = ntspawn(program, argv, envp, v, 0, 0, true, 0, 0, &si, &pi);
|
||||
if (rc == -1) {
|
||||
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
|
||||
__imp_ExitProcess(11);
|
||||
if (__imp_GetLastError() == kNtErrorSharingViolation) {
|
||||
__imp_ExitProcess(SIGVTALRM); // is ETXTBSY
|
||||
} else {
|
||||
__imp_ExitProcess(127 << 8);
|
||||
}
|
||||
}
|
||||
PurgeHandle(pi.hThread);
|
||||
|
||||
// retreat to original win32-provided stack memory
|
||||
__switch_stacks(pi.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack);
|
||||
}
|
||||
|
||||
// child is in same process group so wait for it to get killed by this
|
||||
__msabi static keywords bool32 sys_execve_nt_event(uint32_t dwCtrlType) {
|
||||
return true; // tell win32 we handled signal
|
||||
}
|
||||
|
||||
// this function runs on the original tiny stack that windows gave us
|
||||
// we need to keep the original process alive simply to pass an int32
|
||||
// so we unmap all memory to avoid getting a double whammy after fork
|
||||
static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) {
|
||||
uint32_t i, dwExitCode;
|
||||
|
||||
// close more handles
|
||||
__imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1);
|
||||
PurgeThread(g_fds.stdin.thread); // wasn't inherited by ntspawn
|
||||
PurgeHandle(g_fds.stdin.reader); // wasn't inherited by ntspawn
|
||||
PurgeHandle(g_fds.stdin.writer); // wasn't inherited by ntspawn
|
||||
PurgeHandle(g_fds.p[0].handle); // was inherited via startinfo
|
||||
PurgeHandle(g_fds.p[1].handle); // was inherited via startinfo
|
||||
PurgeHandle(g_fds.p[2].handle); // was inherited via startinfo
|
||||
if (_weaken(__klog_handle)) {
|
||||
PurgeHandle(*_weaken(__klog_handle)); // wasn't inherited by ntspawn
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// zombify this process which lingers on to relay the status code
|
||||
// free all the memory mmap created
|
||||
for (i = 0; i < _mmi.i; ++i) {
|
||||
__imp_UnmapViewOfFile((void *)((uintptr_t)_mmi.p[i].x << 16));
|
||||
}
|
||||
|
||||
PurgeHandle(procinfo.hThread);
|
||||
__switch_stacks(procinfo.hProcess, 0, 0, 0, sys_execve_nt_relay, __oldstack);
|
||||
// wait for process to terminate
|
||||
//
|
||||
// WaitForSingleObject can return kNtWaitAbandoned which MSDN
|
||||
// describes as a "sort of" successful status which indicates
|
||||
// someone else didn't free a mutex and you should check that
|
||||
// persistent resources haven't been left corrupted. not sure
|
||||
// what those resources would be for process objects, however
|
||||
// this status has actually been observed when waiting on 'em
|
||||
do {
|
||||
if (__imp_WaitForSingleObject(h, -1) == kNtWaitFailed) {
|
||||
notpossible;
|
||||
}
|
||||
if (!__imp_GetExitCodeProcess(h, &dwExitCode)) {
|
||||
notpossible;
|
||||
}
|
||||
} while (dwExitCode == kNtStillActive);
|
||||
|
||||
// propagate child exit status to parent
|
||||
__imp_ExitProcess(dwExitCode);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
|
|
@ -19,5 +19,5 @@
|
|||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
unsigned __sighandrvas[NSIG];
|
||||
unsigned __sighandflags[NSIG];
|
||||
unsigned __sighandrvas[NSIG + 1];
|
||||
unsigned __sighandflags[NSIG + 1];
|
||||
|
|
|
@ -641,6 +641,7 @@ static int ioctl_siocgifflags(int fd, void *arg) {
|
|||
* - `FIONBIO` isn't polyfilled; use `fcntl(F_SETFL, O_NONBLOCK)`
|
||||
* - `FIOCLEX` isn't polyfilled; use `fcntl(F_SETFD, FD_CLOEXEC)`
|
||||
* - `FIONCLEX` isn't polyfilled; use `fcntl(F_SETFD, 0)`
|
||||
* - `TIOCSCTTY` isn't polyfilled; use `login_tty()`
|
||||
* - `TCGETS` isn't polyfilled; use tcgetattr()
|
||||
* - `TCSETS` isn't polyfilled; use tcsetattr()
|
||||
* - `TCSETSW` isn't polyfilled; use tcsetattr()
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
*
|
||||
* The order of precedence is:
|
||||
*
|
||||
* - $TMPDIR/
|
||||
* - GetTempPath()
|
||||
* - /tmp/
|
||||
* - $TMPDIR/ is always favored if defined
|
||||
* - GetTempPath(), for the New Technology
|
||||
* - /tmp/ to make security scene go crazy
|
||||
*
|
||||
* This guarantees trailing slash.
|
||||
* We also guarantee `kTmpPath` won't be longer than `PATH_MAX / 2`.
|
||||
* This guarantees an absolute path with a trailing slash. We also
|
||||
* ensure `kTmpPath` isn't longer than `PATH_MAX-NAME_MAX`.
|
||||
*/
|
||||
char kTmpPath[PATH_MAX];
|
||||
|
||||
|
@ -48,15 +48,20 @@ __attribute__((__constructor__)) static void kTmpPathInit(void) {
|
|||
uint32_t n;
|
||||
char16_t path16[PATH_MAX];
|
||||
|
||||
if ((s = getenv("TMPDIR")) && (n = strlen(s)) < PATH_MAX / 2) {
|
||||
if (n) memcpy(kTmpPath, s, n);
|
||||
if (n && kTmpPath[n - 1] != '/') {
|
||||
kTmpPath[n + 0] = '/';
|
||||
kTmpPath[n + 1] = 0;
|
||||
if ((s = getenv("TMPDIR"))) {
|
||||
if (*s != '/') {
|
||||
if (!getcwd(kTmpPath, PATH_MAX)) {
|
||||
goto GiveUp;
|
||||
}
|
||||
strlcat(kTmpPath, "/", sizeof(kTmpPath));
|
||||
}
|
||||
strlcat(kTmpPath, s, sizeof(kTmpPath));
|
||||
if (strlcat(kTmpPath, "/", sizeof(kTmpPath)) < PATH_MAX - NAME_MAX) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GiveUp:
|
||||
if (IsWindows() &&
|
||||
((n = GetTempPath(ARRAYLEN(path16), path16)) && n < ARRAYLEN(path16))) {
|
||||
// turn c:\foo\bar\ into c:/foo/bar/
|
||||
|
|
|
@ -17,18 +17,22 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/pushpop.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/enum/processcreationflags.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/memory.h"
|
||||
#include "libc/nt/process.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/processinformation.h"
|
||||
#include "libc/nt/struct/securityattributes.h"
|
||||
#include "libc/nt/struct/startupinfo.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
struct SpawnBlock {
|
||||
union {
|
||||
|
@ -95,6 +99,8 @@ textwindows int ntspawn(
|
|||
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
|
||||
opt_out_lpProcessInformation)) {
|
||||
rc = 0;
|
||||
} else if (GetLastError() == kNtErrorSharingViolation) {
|
||||
etxtbsy();
|
||||
}
|
||||
if (block) UnmapViewOfFile(block);
|
||||
if (handle) CloseHandle(handle);
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
/**
|
||||
* Changes process group for process.
|
||||
*
|
||||
* @param pid is process id (may be zero for current process)
|
||||
* @param pgid is process group id (may be zero for current process)
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @vforksafe
|
||||
*/
|
||||
int setpgid(int pid, int pgid) {
|
||||
|
@ -35,16 +39,14 @@ int setpgid(int pid, int pgid) {
|
|||
} else {
|
||||
me = getpid();
|
||||
if ((!pid || pid == me) && (!pgid || pgid == me)) {
|
||||
/*
|
||||
* "When a process is created with CREATE_NEW_PROCESS_GROUP
|
||||
* specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
|
||||
* is made on behalf of the new process; this means that the new
|
||||
* process has CTRL+C disabled. This lets shells handle CTRL+C
|
||||
* themselves, and selectively pass that signal on to
|
||||
* sub-processes. CTRL+BREAK is not disabled, and may be used to
|
||||
* interrupt the process/process group."
|
||||
* ──Quoth MSDN § CreateProcessW()
|
||||
*/
|
||||
// "When a process is created with kNtCreateNewProcessGroup
|
||||
// specified, an implicit call to SetConsoleCtrlHandler(NULL,TRUE)
|
||||
// is made on behalf of the new process; this means that the new
|
||||
// process has CTRL+C disabled. This lets shells handle CTRL+C
|
||||
// themselves, and selectively pass that signal on to
|
||||
// sub-processes. CTRL+BREAK is not disabled, and may be used to
|
||||
// interrupt the process/process group."
|
||||
// ──Quoth MSDN § CreateProcessW()
|
||||
if (SetConsoleCtrlHandler(0, 1)) {
|
||||
rc = 0;
|
||||
} else {
|
||||
|
|
|
@ -23,14 +23,16 @@
|
|||
|
||||
/**
|
||||
* Creates session and sets the process group id.
|
||||
*
|
||||
* @return new session id, or -1 w/ errno
|
||||
* @raise EPERM if already the leader
|
||||
*/
|
||||
int setsid(void) {
|
||||
int rc;
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
rc = sys_setsid();
|
||||
} else {
|
||||
rc = 0;
|
||||
rc = getpid();
|
||||
}
|
||||
STRACE("setsid() → %d% m", rc);
|
||||
return rc;
|
||||
|
|
|
@ -59,21 +59,14 @@ static void sigaltstack2linux(struct sigaltstack *linux,
|
|||
* struct sigaction sa;
|
||||
* struct sigaltstack ss;
|
||||
* ss.ss_flags = 0;
|
||||
* ss.ss_sp = NewCosmoStack();
|
||||
* ss.ss_size = GetStackSize();
|
||||
* ss.ss_sp = mmap(0, GetStackSize(), PROT_READ | PROT_WRITE,
|
||||
* MAP_STACK | MAP_ANONYMOUS, -1, 0);
|
||||
* sigaltstack(&ss, 0);
|
||||
* sigemptyset(&sa.ss_mask);
|
||||
* sa.sa_flags = SA_ONSTACK;
|
||||
* sa.sa_handler = OnStackOverflow;
|
||||
* __cxa_atexit(free, ss[0].ss_sp, 0);
|
||||
* sigemptyset(&sa.ss_mask);
|
||||
* sigaltstack(&ss, 0);
|
||||
* sigaction(SIGSEGV, &sa, 0);
|
||||
*
|
||||
* It's strongly recommended that you allocate a stack with the same
|
||||
* size as GetStackSize() and that it have GetStackSize() alignment.
|
||||
* Otherwise some of your runtime support code (e.g. ftrace stack use
|
||||
* logging, kprintf() memory safety) won't be able to work as well.
|
||||
*
|
||||
* @param neu if non-null will install new signal alt stack
|
||||
* @param old if non-null will receive current signal alt stack
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
|
@ -85,9 +78,8 @@ int sigaltstack(const struct sigaltstack *neu, struct sigaltstack *old) {
|
|||
void *b;
|
||||
const void *a;
|
||||
struct sigaltstack_bsd bsd;
|
||||
if (IsAsan() && ((old && __asan_check(old, sizeof(*old)).kind) ||
|
||||
(neu && (__asan_check(neu, sizeof(*neu)).kind ||
|
||||
__asan_check(neu->ss_sp, neu->ss_size).kind)))) {
|
||||
if (IsAsan() && ((old && !__asan_is_valid(old, sizeof(*old))) ||
|
||||
(neu && !__asan_is_valid(neu, sizeof(*neu))))) {
|
||||
rc = efault();
|
||||
} else if (neu && neu->ss_size < MINSIGSTKSZ) {
|
||||
rc = enomem();
|
||||
|
|
|
@ -41,9 +41,9 @@ privileged void __sigenter_freebsd(int sig, struct siginfo_freebsd *freebsdinfo,
|
|||
ucontext_t uc;
|
||||
siginfo_t si;
|
||||
} g;
|
||||
rva = __sighandrvas[sig & (NSIG - 1)];
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig & (NSIG - 1)];
|
||||
flags = __sighandflags[sig];
|
||||
if (~flags & SA_SIGINFO) {
|
||||
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
|
||||
} else {
|
||||
|
|
|
@ -33,9 +33,9 @@
|
|||
|
||||
privileged void __sigenter_wsl(int sig, struct siginfo *info, ucontext_t *ctx) {
|
||||
int i, rva, flags;
|
||||
rva = __sighandrvas[sig & (NSIG - 1)];
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig & (NSIG - 1)];
|
||||
flags = __sighandflags[sig];
|
||||
// WSL1 doesn't set the fpregs field.
|
||||
// https://github.com/microsoft/WSL/issues/2555
|
||||
if ((flags & SA_SIGINFO) && UNLIKELY(!ctx->uc_mcontext.fpregs)) {
|
||||
|
|
|
@ -39,9 +39,9 @@ privileged void __sigenter_netbsd(int sig, struct siginfo_netbsd *si,
|
|||
int rva, flags;
|
||||
ucontext_t uc;
|
||||
struct siginfo si2;
|
||||
rva = __sighandrvas[sig & (NSIG - 1)];
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig & (NSIG - 1)];
|
||||
flags = __sighandflags[sig];
|
||||
if (~flags & SA_SIGINFO) {
|
||||
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
|
||||
} else {
|
||||
|
|
|
@ -41,9 +41,9 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo,
|
|||
ucontext_t uc;
|
||||
struct siginfo si;
|
||||
} g;
|
||||
rva = __sighandrvas[sig & (NSIG - 1)];
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig & (NSIG - 1)];
|
||||
flags = __sighandflags[sig];
|
||||
if (~flags & SA_SIGINFO) {
|
||||
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
|
||||
} else {
|
||||
|
|
|
@ -493,9 +493,9 @@ privileged void __sigenter_xnu(void *fn, int infostyle, int sig,
|
|||
ucontext_t uc;
|
||||
siginfo_t si;
|
||||
} g;
|
||||
rva = __sighandrvas[sig & (NSIG - 1)];
|
||||
rva = __sighandrvas[sig];
|
||||
if (rva >= kSigactionMinRva) {
|
||||
flags = __sighandflags[sig & (NSIG - 1)];
|
||||
flags = __sighandflags[sig];
|
||||
if (~flags & SA_SIGINFO) {
|
||||
((sigaction_f)(__executable_start + rva))(sig, 0, 0);
|
||||
} else {
|
||||
|
|
|
@ -8,10 +8,10 @@ COSMOPOLITAN_C_START_
|
|||
|
||||
extern int __vforked;
|
||||
extern bool __time_critical;
|
||||
extern unsigned __sighandrvas[NSIG];
|
||||
extern unsigned __sighandflags[NSIG];
|
||||
extern pthread_mutex_t __fds_lock_obj;
|
||||
extern pthread_mutex_t __sig_lock_obj;
|
||||
extern unsigned __sighandrvas[NSIG + 1];
|
||||
extern unsigned __sighandflags[NSIG + 1];
|
||||
extern const struct NtSecurityAttributes kNtIsInheritable;
|
||||
|
||||
void __fds_lock(void);
|
||||
|
|
|
@ -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 2021 Justine Alexandra Roberts Tunney │
|
||||
│ Copyright 2023 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,24 +16,37 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "net/https/sslcache.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
/**
|
||||
* Returns recommended path argument for CreateSslCache().
|
||||
* @return pointer to static memory
|
||||
* Associates session with controlling tty.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EBADF if `fd` isn't an open file descriptor
|
||||
* @raise ENOTTY if `fd` is isn't controlling teletypewriter
|
||||
* @raise EINVAL if `pid` isn't session id associated with this process
|
||||
* @raise EPERM if calling process isn't the session leader
|
||||
* @raise ENOSYS on Windows and Bare Metal
|
||||
*/
|
||||
char *GetSslCacheFile(void) {
|
||||
static char sslcachefile[PATH_MAX];
|
||||
if (snprintf(sslcachefile, sizeof(sslcachefile), "%s/%s.sslcache",
|
||||
firstnonnull(getenv("TMPDIR"), "/tmp"),
|
||||
getenv("USER")) < ARRAYLEN(sslcachefile)) {
|
||||
return sslcachefile;
|
||||
int tcsetsid(int fd, int pid) {
|
||||
int rc;
|
||||
if (fd < 0) {
|
||||
rc = ebadf();
|
||||
} else if (IsWindows() || IsMetal()) {
|
||||
rc = enosys();
|
||||
} else if (pid != sys_getsid(0)) {
|
||||
rc = einval();
|
||||
} else if (fd < g_fds.n && g_fds.p[fd].kind == kFdZip) {
|
||||
rc = enotty();
|
||||
} else {
|
||||
return 0;
|
||||
rc = sys_ioctl(fd, TIOCSCTTY, 0);
|
||||
}
|
||||
STRACE("tcsetsid(%d, %d) → %d% m", fd, pid, rc);
|
||||
return rc;
|
||||
}
|
|
@ -28,6 +28,7 @@ int tcgetsid(int);
|
|||
int tcgetpgrp(int);
|
||||
int tcflow(int, int);
|
||||
int tcflush(int, int);
|
||||
int tcsetsid(int, int);
|
||||
int tcsetpgrp(int, int);
|
||||
int tcsendbreak(int, int);
|
||||
void cfmakeraw(struct termios *);
|
||||
|
|
|
@ -45,7 +45,7 @@ int _mkstemp(char *, int);
|
|||
* This creates a secure temporary file inside $TMPDIR. If it isn't
|
||||
* defined, then /tmp is used on UNIX and GetTempPath() is used on the
|
||||
* New Technology. This resolution of $TMPDIR happens once in a ctor,
|
||||
* which is copied to the `kTmpDir` global.
|
||||
* which is copied to the `kTmpPath` global.
|
||||
*
|
||||
* Once close() is called, the returned file is guaranteed to be deleted
|
||||
* automatically. On UNIX the file is unlink()'d before this function
|
||||
|
|
|
@ -118,6 +118,8 @@ static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
|
|||
return echild();
|
||||
}
|
||||
}
|
||||
|
||||
// wait for one of the processes to terminate
|
||||
dwExitCode = kNtStillActive;
|
||||
if (options & WNOHANG) {
|
||||
i = WaitForMultipleObjects(count, handles, false, 0);
|
||||
|
@ -132,12 +134,20 @@ static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
|
|||
}
|
||||
}
|
||||
if (i == kNtWaitFailed) {
|
||||
STRACE("%s failed %u", "WaitForMultipleObjects", GetLastError());
|
||||
return __winerr();
|
||||
notpossible;
|
||||
}
|
||||
|
||||
// WaitForMultipleObjects can say kNtWaitAbandoned which MSDN
|
||||
// describes as a "sort of" successful status which indicates
|
||||
// someone else didn't free a mutex and you should check that
|
||||
// persistent resources haven't been left corrupted. not sure
|
||||
// what those resources would be for process objects, however
|
||||
// this status has actually been observed when waiting on 'em
|
||||
i &= ~kNtWaitAbandoned;
|
||||
|
||||
// this is where things get especially hairy. see exit() doc
|
||||
if (!GetExitCodeProcess(handles[i], &dwExitCode)) {
|
||||
STRACE("%s failed %u", "GetExitCodeProcess", GetLastError());
|
||||
return __winerr();
|
||||
notpossible;
|
||||
}
|
||||
if (dwExitCode == kNtStillActive) {
|
||||
return -2;
|
||||
|
@ -145,6 +155,8 @@ static textwindows int sys_wait4_nt_impl(int *pid, int *opt_out_wstatus,
|
|||
if (dwExitCode == 0xc9af3d51u) {
|
||||
dwExitCode = kNtStillActive;
|
||||
}
|
||||
|
||||
// now pass along the result
|
||||
if (opt_out_wstatus) {
|
||||
*opt_out_wstatus = dwExitCode;
|
||||
}
|
||||
|
|
|
@ -19,44 +19,22 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/exceptionhandleractions.h"
|
||||
#include "libc/nt/enum/signal.h"
|
||||
#include "libc/nt/enum/status.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "libc/thread/tls2.internal.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
// win32 calls this; we're running inside the thread that crashed
|
||||
__msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
|
||||
int sig, code;
|
||||
struct CosmoTib *tib;
|
||||
static bool noreentry;
|
||||
noreentry = true;
|
||||
|
||||
if ((tib = __tls_enabled ? __get_tls() : 0)) {
|
||||
if (~tib->tib_flags & TIB_FLAG_WINCRASHING) {
|
||||
tib->tib_flags |= TIB_FLAG_WINCRASHING;
|
||||
} else {
|
||||
ExitProcess(SIGSEGV);
|
||||
}
|
||||
} else {
|
||||
if (!noreentry) {
|
||||
noreentry = true;
|
||||
} else {
|
||||
ExitProcess(SIGSEGV);
|
||||
}
|
||||
}
|
||||
|
||||
switch (ep->ExceptionRecord->ExceptionCode) {
|
||||
case kNtSignalBreakpoint:
|
||||
|
@ -133,7 +111,8 @@ __msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
|
|||
break;
|
||||
}
|
||||
|
||||
STRACE("wincrash %G rip %x bt %s", sig, ep->ContextRecord->Rip,
|
||||
STRACE("win32 vectored exception raising 0x%08Xu %G rip %x bt %s",
|
||||
ep->ExceptionRecord->ExceptionCode, sig, ep->ContextRecord->Rip,
|
||||
DescribeBacktrace((struct StackFrame *)ep->ContextRecord->Rbp));
|
||||
|
||||
if (__sighandflags[sig] & SA_SIGINFO) {
|
||||
|
@ -143,11 +122,6 @@ __msabi unsigned __wincrash(struct NtExceptionPointers *ep) {
|
|||
__sig_handle(kSigOpUnmaskable, sig, code, 0);
|
||||
}
|
||||
|
||||
noreentry = false;
|
||||
if (tib) {
|
||||
tib->tib_flags &= ~TIB_FLAG_WINCRASHING;
|
||||
}
|
||||
|
||||
return kNtExceptionContinueExecution;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ textwindows static char16_t *CreateStdinPipeName(char16_t *a, int64_t h) {
|
|||
// this makes it possible for our read() implementation to periodically
|
||||
// poll for signals while performing a blocking overlapped io operation
|
||||
textwindows void WinMainStdin(void) {
|
||||
uint32_t conmode;
|
||||
char16_t pipename[64];
|
||||
int64_t hStdin, hWriter, hReader, hThread, hSemaphore;
|
||||
if (!SupportsWindows()) return;
|
||||
|
@ -137,6 +138,10 @@ textwindows void WinMainStdin(void) {
|
|||
Log("<stdin> GetStdHandle failed\n");
|
||||
return;
|
||||
}
|
||||
if (!__imp_GetConsoleMode(hStdin, &conmode)) {
|
||||
Log("<stdin> stdin not a console\n");
|
||||
return;
|
||||
}
|
||||
CreateStdinPipeName(pipename, hStdin);
|
||||
hReader = __imp_CreateFileW(pipename, kNtGenericRead, 0, 0, kNtOpenExisting,
|
||||
kNtFileFlagOverlapped, 0);
|
||||
|
|
|
@ -17,8 +17,11 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/rlimit.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
|
@ -40,6 +43,7 @@
|
|||
#include "libc/nt/enum/version.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/symbols.internal.h"
|
||||
#include "libc/stdckdint.h"
|
||||
#include "libc/str/str.h"
|
||||
|
@ -47,6 +51,8 @@
|
|||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/rlim.h"
|
||||
#include "libc/sysv/consts/rlimit.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/tls.h"
|
||||
#include "third_party/dlmalloc/dlmalloc.h"
|
||||
|
@ -910,7 +916,7 @@ static __wur __asan_die_f *__asan_report(const void *addr, int size,
|
|||
*p = 0;
|
||||
kprintf("%s", buf);
|
||||
__asan_report_memory_origin(addr, size, kind);
|
||||
kprintf("\nthe crash was caused by\n");
|
||||
kprintf("\nthe crash was caused by %s\n", program_invocation_name);
|
||||
ftrace_enabled(+1);
|
||||
return __asan_die();
|
||||
}
|
||||
|
@ -1428,32 +1434,6 @@ static size_t __asan_strlen(const char *s) {
|
|||
return i;
|
||||
}
|
||||
|
||||
static textstartup void __asan_shadow_string(char *s) {
|
||||
__asan_map_shadow((intptr_t)s, __asan_strlen(s) + 1);
|
||||
}
|
||||
|
||||
static textstartup void __asan_shadow_auxv(intptr_t *auxv) {
|
||||
size_t i;
|
||||
for (i = 0; auxv[i]; i += 2) {
|
||||
if (_weaken(AT_RANDOM) && auxv[i] == *_weaken(AT_RANDOM)) {
|
||||
__asan_map_shadow(auxv[i + 1], 16);
|
||||
} else if (_weaken(AT_EXECFN) && auxv[i] == *_weaken(AT_EXECFN)) {
|
||||
__asan_shadow_string((char *)auxv[i + 1]);
|
||||
} else if (_weaken(AT_PLATFORM) && auxv[i] == *_weaken(AT_PLATFORM)) {
|
||||
__asan_shadow_string((char *)auxv[i + 1]);
|
||||
}
|
||||
}
|
||||
__asan_map_shadow((uintptr_t)auxv, (i + 2) * sizeof(intptr_t));
|
||||
}
|
||||
|
||||
static textstartup void __asan_shadow_string_list(char **list) {
|
||||
size_t i;
|
||||
for (i = 0; list[i]; ++i) {
|
||||
__asan_shadow_string(list[i]);
|
||||
}
|
||||
__asan_map_shadow((uintptr_t)list, (i + 1) * sizeof(char *));
|
||||
}
|
||||
|
||||
static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m,
|
||||
size_t i) {
|
||||
uintptr_t x, y;
|
||||
|
@ -1465,18 +1445,94 @@ static textstartup void __asan_shadow_mapping(struct MemoryIntervals *m,
|
|||
}
|
||||
}
|
||||
|
||||
static textstartup void __asan_shadow_existing_mappings(void) {
|
||||
static textstartup char *__asan_get_last_string(char **list) {
|
||||
char *res = 0;
|
||||
for (int i = 0; list[i]; ++i) {
|
||||
res = list[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static textstartup uintptr_t __asan_get_stack_top(int argc, char **argv,
|
||||
char **envp,
|
||||
unsigned long *auxv,
|
||||
long pagesz) {
|
||||
uintptr_t top;
|
||||
const char *s;
|
||||
if ((s = __asan_get_last_string(envp)) ||
|
||||
(s = __asan_get_last_string(argv))) {
|
||||
top = (uintptr_t)s + __asan_strlen(s);
|
||||
} else {
|
||||
unsigned long *xp = auxv;
|
||||
while (*xp) xp += 2;
|
||||
top = (uintptr_t)xp;
|
||||
}
|
||||
return (top + (pagesz - 1)) & -pagesz;
|
||||
}
|
||||
|
||||
static textstartup void __asan_shadow_existing_mappings(int argc, char **argv,
|
||||
char **envp,
|
||||
unsigned long *auxv) {
|
||||
__asan_shadow_mapping(&_mmi, 0);
|
||||
__asan_map_shadow(GetStackAddr(), GetStackSize());
|
||||
__asan_poison((void *)GetStackAddr(), getauxval(AT_PAGESZ),
|
||||
kAsanStackOverflow);
|
||||
|
||||
// WinMain() maps its own stack and its shadow too
|
||||
if (IsWindows()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get microprocessor page size
|
||||
long pagesz = 4096;
|
||||
for (int i = 0; auxv[i]; i += 2) {
|
||||
if (auxv[i] == AT_PAGESZ) {
|
||||
pagesz = auxv[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
// get configured stack size of main thread
|
||||
// supported platforms use defaults ranging from 4mb to 512mb
|
||||
struct rlimit rlim;
|
||||
uintptr_t stack_size = 4 * 1024 * 1024;
|
||||
if (!sys_getrlimit(RLIMIT_STACK, &rlim) && //
|
||||
rlim.rlim_cur > 0 && rlim.rlim_cur < RLIM_INFINITY) {
|
||||
stack_size = (rlim.rlim_cur + (pagesz - 1)) & -pagesz;
|
||||
}
|
||||
|
||||
// Every UNIX system in our support vector creates arg blocks like:
|
||||
//
|
||||
// <HIGHEST-STACK-ADDRESS>
|
||||
// last environ string
|
||||
// ...
|
||||
// first environ string
|
||||
// ...
|
||||
// auxiliary value pointers
|
||||
// environ pointers
|
||||
// argument pointers
|
||||
// argument count
|
||||
// --- %rsp _start()
|
||||
// ...
|
||||
// ...
|
||||
// ... program's stack
|
||||
// ...
|
||||
// ...
|
||||
// <LOWEST-STACK-ADDRESS>
|
||||
//
|
||||
// The region of memory between highest and lowest can be computed
|
||||
// across all supported platforms ±1 page accuracy as the distance
|
||||
// between the last character of the last environ variable rounded
|
||||
// up to the microprocessor page size (this computes the top addr)
|
||||
// and the bottom is computed by subtracting RLIMIT_STACK rlim_cur
|
||||
// It's simple but gets tricky if we consider environ can be empty
|
||||
uintptr_t stack_top = __asan_get_stack_top(argc, argv, envp, auxv, pagesz);
|
||||
uintptr_t stack_bot = stack_top - stack_size;
|
||||
__asan_map_shadow(stack_bot, stack_top - stack_bot);
|
||||
__asan_poison((void *)stack_bot, GetGuardSize(), kAsanStackOverflow);
|
||||
}
|
||||
|
||||
forceinline ssize_t __write_str(const char *s) {
|
||||
return sys_write(2, s, __asan_strlen(s));
|
||||
}
|
||||
|
||||
void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) {
|
||||
void __asan_init(int argc, char **argv, char **envp, unsigned long *auxv) {
|
||||
static bool once;
|
||||
if (!_cmpxchg(&once, false, true)) return;
|
||||
if (IsWindows() && NtGetVersion() < kNtVersionWindows10) {
|
||||
|
@ -1493,16 +1549,13 @@ void __asan_init(int argc, char **argv, char **envp, intptr_t *auxv) {
|
|||
REQUIRE(dlmemalign);
|
||||
REQUIRE(dlmalloc_usable_size);
|
||||
}
|
||||
__asan_shadow_existing_mappings();
|
||||
__asan_shadow_existing_mappings(argc, argv, envp, auxv);
|
||||
__asan_map_shadow((uintptr_t)__executable_start, _end - __executable_start);
|
||||
__asan_map_shadow(0, 4096);
|
||||
__asan_poison((void *)__veil("r", 0L), getauxval(AT_PAGESZ), kAsanNullPage);
|
||||
if (!IsWindows()) {
|
||||
sys_mprotect((void *)0x7fff8000, 0x10000, PROT_READ);
|
||||
}
|
||||
__asan_shadow_string_list(argv);
|
||||
__asan_shadow_string_list(envp);
|
||||
__asan_shadow_auxv(auxv);
|
||||
__asan_install_malloc_hooks();
|
||||
STRACE(" _ ____ _ _ _ ");
|
||||
STRACE(" / \\ / ___| / \\ | \\ | |");
|
||||
|
|
|
@ -18,22 +18,30 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
|
||||
#define N 64
|
||||
#define N 100
|
||||
|
||||
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
|
||||
|
||||
const char *(DescribeBacktrace)(char buf[N], struct StackFrame *fr) {
|
||||
int o = 0;
|
||||
dontinstrument dontasan const char *(DescribeBacktrace)(char buf[N],
|
||||
struct StackFrame *fr) {
|
||||
bool gotsome = false;
|
||||
char *p = buf;
|
||||
char *pe = p + N;
|
||||
while (fr) {
|
||||
if (gotsome) {
|
||||
append(" ");
|
||||
if (p + 1 < pe) {
|
||||
*p++ = ' ';
|
||||
*p = 0;
|
||||
}
|
||||
} else {
|
||||
gotsome = true;
|
||||
}
|
||||
append("%x", fr->addr);
|
||||
if (p + 17 <= pe) {
|
||||
p = __hexcpy(p, fr->addr);
|
||||
}
|
||||
fr = fr->next;
|
||||
}
|
||||
return buf;
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
const char *DescribeBacktrace(char[64], struct StackFrame *);
|
||||
#define DescribeBacktrace(x) DescribeBacktrace(alloca(64), x)
|
||||
const char *DescribeBacktrace(char[100], struct StackFrame *);
|
||||
#define DescribeBacktrace(x) DescribeBacktrace(alloca(100), x)
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/asancodes.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/directmap.internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -30,7 +32,7 @@
|
|||
|
||||
#define G FRAMESIZE
|
||||
|
||||
static void *_mapframe(void *p, int f) {
|
||||
static dontasan void *_mapframe(void *p, int f) {
|
||||
int rc, prot, flags;
|
||||
struct DirectMap dm;
|
||||
prot = PROT_READ | PROT_WRITE;
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
int getpid(void) {
|
||||
dontasan int getpid(void) {
|
||||
int rc;
|
||||
if (IsMetal()) {
|
||||
rc = 1;
|
||||
|
|
|
@ -19,10 +19,13 @@
|
|||
#include "libc/intrin/kmalloc.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/extend.internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -63,7 +66,7 @@ void __kmalloc_unlock(void) {
|
|||
* @return zero-initialized memory on success, or null w/ errno
|
||||
* @raise ENOMEM if we require more vespene gas
|
||||
*/
|
||||
void *kmalloc(size_t size) {
|
||||
dontasan void *kmalloc(size_t size) {
|
||||
char *p, *e;
|
||||
size_t i, n, t;
|
||||
n = ROUNDUP(size + (IsAsan() * 8), KMALLOC_ALIGN);
|
||||
|
|
|
@ -325,13 +325,13 @@ privileged long kloghandle(void) {
|
|||
long proc;
|
||||
proc = __imp_GetCurrentProcess();
|
||||
hand = __imp_GetStdHandle(kNtStdErrorHandle);
|
||||
__imp_DuplicateHandle(proc, hand, proc, &hand, 0, true,
|
||||
__imp_DuplicateHandle(proc, hand, proc, &hand, 0, false,
|
||||
kNtDuplicateSameAccess);
|
||||
} else if (n && n < 512) {
|
||||
hand = __imp_CreateFileW(
|
||||
path, kNtFileAppendData,
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete,
|
||||
&kNtIsInheritable, kNtOpenAlways, kNtFileAttributeNormal, 0);
|
||||
kNtFileShareRead | kNtFileShareWrite | kNtFileShareDelete, 0,
|
||||
kNtOpenAlways, kNtFileAttributeNormal, 0);
|
||||
} else {
|
||||
hand = -1; // KPRINTF_LOG was empty string or too long
|
||||
}
|
||||
|
|
|
@ -16,20 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
#if SupportsMetal()
|
||||
__static_yoink("_idt");
|
||||
|
@ -37,29 +35,18 @@ __static_yoink("_idt");
|
|||
|
||||
/**
|
||||
* Aborts process after printing a backtrace.
|
||||
*
|
||||
* If a debugger is present then this will trigger a breakpoint.
|
||||
*/
|
||||
relegated wontreturn void __die(void) {
|
||||
/* asan runtime depends on this function */
|
||||
int me, owner;
|
||||
static atomic_int once;
|
||||
owner = 0;
|
||||
me = __tls_enabled ? __get_tls()->tib_tid : sys_gettid();
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
||||
if (__vforked ||
|
||||
atomic_compare_exchange_strong_explicit(
|
||||
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
|
||||
__restore_tty();
|
||||
if (IsDebuggerPresent(false)) {
|
||||
DebugBreak();
|
||||
}
|
||||
ShowBacktrace(2, __builtin_frame_address(0));
|
||||
_Exit(77);
|
||||
} else if (owner == me) {
|
||||
kprintf("die failed while dying\n");
|
||||
_Exit(79);
|
||||
} else {
|
||||
_Exit1(79);
|
||||
}
|
||||
relegated dontasan wontreturn void __die(void) {
|
||||
|
||||
// print vital error nubers reliably
|
||||
// the surface are of code this calls is small and audited
|
||||
kprintf("\r\n\e[1;31m__die %s pid %d tid %d bt %s\e[0m\n",
|
||||
program_invocation_short_name, getpid(), sys_gettid(),
|
||||
DescribeBacktrace(__builtin_frame_address(0)));
|
||||
|
||||
// print much friendlier backtrace less reliably
|
||||
// we're in a broken runtime state and so much can go wrong
|
||||
__restore_tty();
|
||||
ShowBacktrace(2, __builtin_frame_address(0));
|
||||
_Exit(77);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,12 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
__funline unsigned long __strlen(const char *s) {
|
||||
unsigned long n = 0;
|
||||
while (*s++) ++n;
|
||||
return n;
|
||||
}
|
||||
|
||||
__funline int __strcmp(const char *l, const char *r) {
|
||||
size_t i = 0;
|
||||
while (l[i] == r[i] && r[i]) ++i;
|
||||
|
@ -24,6 +30,15 @@ __funline char *__stpcpy(char *d, const char *s) {
|
|||
}
|
||||
}
|
||||
|
||||
__funline long __write_linux(int fd, const void *p, long n) {
|
||||
long ax = 1;
|
||||
asm volatile("syscall"
|
||||
: "+a"(ax)
|
||||
: "D"(fd), "S"(p), "d"(n)
|
||||
: "rcx", "r11", "memory");
|
||||
return ax;
|
||||
}
|
||||
|
||||
__funline void *__repstosb(void *di, char al, size_t cx) {
|
||||
#if defined(__x86__) && defined(__GNUC__) && !defined(__STRICT_ANSI__)
|
||||
asm("rep stosb"
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/utsname.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
|
@ -29,6 +30,7 @@
|
|||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kmalloc.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
@ -259,113 +261,42 @@ relegated void ShowCrashReport(int err, int sig, struct siginfo *si,
|
|||
kprintf("\n");
|
||||
}
|
||||
|
||||
static wontreturn relegated dontinstrument void __minicrash(int sig,
|
||||
struct siginfo *si,
|
||||
ucontext_t *ctx,
|
||||
const char *kind) {
|
||||
kprintf("\n"
|
||||
"\n"
|
||||
"CRASHED %s WITH %G\n"
|
||||
"%s\n"
|
||||
"RIP %x\n"
|
||||
"RSP %x\n"
|
||||
"RBP %x\n"
|
||||
"PID %d\n"
|
||||
"TID %d\n"
|
||||
"\n",
|
||||
kind, sig, __argv[0], ctx ? ctx->uc_mcontext.rip : 0,
|
||||
ctx ? ctx->uc_mcontext.rsp : 0, ctx ? ctx->uc_mcontext.rbp : 0, __pid,
|
||||
__tls_enabled ? __get_tls()->tib_tid : sys_gettid());
|
||||
_Exit(119);
|
||||
static relegated wontreturn void RaiseCrash(int sig) {
|
||||
sigset_t ss;
|
||||
sigfillset(&ss);
|
||||
sigdelset(&ss, sig);
|
||||
sigprocmask(SIG_SETMASK, &ss, 0);
|
||||
signal(sig, SIG_DFL);
|
||||
kill(getpid(), sig);
|
||||
_Exit(128 + sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes in a developer-friendly human-centric way.
|
||||
*
|
||||
* We first try to launch GDB if it's an interactive development
|
||||
* session. Otherwise we show a really good crash report, sort of like
|
||||
* Python, that includes filenames and line numbers. Many editors, e.g.
|
||||
* Emacs, will even recognize its syntax for quick hopping to the
|
||||
* failing line. That's only possible if the the .com.dbg file is in the
|
||||
* same folder. If the concomitant debug binary can't be found, we
|
||||
* simply print addresses which may be cross-referenced using objdump.
|
||||
*
|
||||
* This function never returns, except for traps w/ human supervision.
|
||||
*
|
||||
* @threadsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
relegated void __oncrash_amd64(int sig, struct siginfo *si, void *arg) {
|
||||
int bZero;
|
||||
#ifdef ATTACH_GDB_ON_CRASH
|
||||
intptr_t rip;
|
||||
#endif
|
||||
int me, owner;
|
||||
int gdbpid, err;
|
||||
ucontext_t *ctx = arg;
|
||||
static atomic_int once;
|
||||
static atomic_int once2;
|
||||
STRACE("__oncrash rip %x", ctx->uc_mcontext.rip);
|
||||
|
||||
// print vital error nubers reliably
|
||||
// the surface are of code this calls is small and audited
|
||||
kprintf(
|
||||
"\r\n\e[1;31m__oncrash %G %s pid %d tid %d rip %x bt %s\e[0m\n", sig,
|
||||
program_invocation_short_name, getpid(), sys_gettid(),
|
||||
ctx ? ctx->uc_mcontext.rip : 0,
|
||||
DescribeBacktrace(ctx ? (struct StackFrame *)ctx->uc_mcontext.rbp
|
||||
: (struct StackFrame *)__builtin_frame_address(0)));
|
||||
|
||||
// print friendlier detailed crash report less reliably
|
||||
// we're in a broken runtime state and so much can go wrong
|
||||
ftrace_enabled(-1);
|
||||
strace_enabled(-1);
|
||||
owner = 0;
|
||||
me = __tls_enabled ? __get_tls()->tib_tid : sys_gettid();
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, 0);
|
||||
if (atomic_compare_exchange_strong_explicit(
|
||||
&once, &owner, me, memory_order_relaxed, memory_order_relaxed)) {
|
||||
if (!__vforked) {
|
||||
#ifdef ATTACH_GDB_ON_CRASH
|
||||
rip = ctx ? ctx->uc_mcontext.rip : 0;
|
||||
#endif
|
||||
err = errno;
|
||||
if ((gdbpid = IsDebuggerPresent(true))) {
|
||||
DebugBreak();
|
||||
} else if (__nocolor || g_isrunningundermake) {
|
||||
gdbpid = -1;
|
||||
#if ATTACH_GDB_ON_CRASH
|
||||
} else if (!IsTiny() && IsLinux() && FindDebugBinary() && !__isworker) {
|
||||
// RestoreDefaultCrashSignalHandlers();
|
||||
gdbpid = AttachDebugger(
|
||||
((sig == SIGTRAP || sig == SIGQUIT) &&
|
||||
(rip >= (intptr_t)&__executable_start && rip < (intptr_t)&_etext))
|
||||
? rip
|
||||
: 0);
|
||||
#endif
|
||||
}
|
||||
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
|
||||
__restore_tty();
|
||||
ShowCrashReport(err, sig, si, ctx);
|
||||
_Exit(128 + sig);
|
||||
}
|
||||
atomic_store_explicit(&once, 0, memory_order_relaxed);
|
||||
} else {
|
||||
atomic_store_explicit(&once, 0, memory_order_relaxed);
|
||||
__minicrash(sig, si, ctx, "WHILE VFORKED");
|
||||
}
|
||||
} else if (sig == SIGTRAP) {
|
||||
// chances are IsDebuggerPresent() confused strace w/ gdb
|
||||
goto ItsATrap;
|
||||
} else if (owner == me) {
|
||||
// we crashed while generating a crash report
|
||||
bZero = false;
|
||||
if (atomic_compare_exchange_strong_explicit(
|
||||
&once2, &bZero, true, memory_order_relaxed, memory_order_relaxed)) {
|
||||
__minicrash(sig, si, ctx, "WHILE CRASHING");
|
||||
} else {
|
||||
// somehow __minicrash() crashed not possible
|
||||
for (;;) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// multiple threads have crashed
|
||||
// kill current thread assuming process dies soon
|
||||
// TODO(jart): It'd be nice to report on all threads.
|
||||
_Exit1(8);
|
||||
err = errno;
|
||||
if ((gdbpid = IsDebuggerPresent(true))) {
|
||||
DebugBreak();
|
||||
}
|
||||
if (!(gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT))) {
|
||||
__restore_tty();
|
||||
ShowCrashReport(err, sig, si, ctx);
|
||||
RaiseCrash(sig);
|
||||
}
|
||||
ItsATrap:
|
||||
strace_enabled(+1);
|
||||
ftrace_enabled(+1);
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/aarch64.internal.h"
|
||||
#include "libc/calls/struct/rusage.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
|
@ -63,18 +64,28 @@ struct Buffer {
|
|||
int i;
|
||||
};
|
||||
|
||||
static bool IsCode(uintptr_t p) {
|
||||
static relegated bool IsCode(uintptr_t p) {
|
||||
return __executable_start <= (uint8_t *)p && (uint8_t *)p < _etext;
|
||||
}
|
||||
|
||||
static void Append(struct Buffer *b, const char *fmt, ...) {
|
||||
static relegated void Append(struct Buffer *b, const char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
b->i += kvsnprintf(b->p + b->i, b->n - b->i, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static const char *ColorRegister(int r) {
|
||||
static relegated wontreturn void RaiseCrash(int sig) {
|
||||
sigset_t ss;
|
||||
sigfillset(&ss);
|
||||
sigdelset(&ss, sig);
|
||||
sigprocmask(SIG_SETMASK, &ss, 0);
|
||||
signal(sig, SIG_DFL);
|
||||
kill(getpid(), sig);
|
||||
_Exit(128 + sig);
|
||||
}
|
||||
|
||||
static relegated const char *ColorRegister(int r) {
|
||||
if (__nocolor) return "";
|
||||
switch (r) {
|
||||
case 0: // arg / res
|
||||
|
@ -115,8 +126,8 @@ static const char *ColorRegister(int r) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
||||
const char *debugbin, long addr) {
|
||||
static relegated bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
||||
const char *debugbin, long addr) {
|
||||
ssize_t rc;
|
||||
char *p, *q, buf[128];
|
||||
int j, k, ws, pid, pfd[2];
|
||||
|
@ -167,8 +178,8 @@ static bool AppendFileLine(struct Buffer *b, const char *addr2line,
|
|||
}
|
||||
}
|
||||
|
||||
static char *GetSymbolName(struct SymbolTable *st, int symbol, char **mem,
|
||||
size_t *memsz) {
|
||||
static relegated char *GetSymbolName(struct SymbolTable *st, int symbol,
|
||||
char **mem, size_t *memsz) {
|
||||
char *s, *t;
|
||||
if ((s = __get_symbol_name(st, symbol)) && //
|
||||
s[0] == '_' && s[1] == 'Z' && //
|
||||
|
@ -348,7 +359,7 @@ relegated void __oncrash_arm64(int sig, struct siginfo *si, void *arg) {
|
|||
}
|
||||
sys_write(2, b->p, MIN(b->i, b->n));
|
||||
__print_maps();
|
||||
_Exit(128 + sig);
|
||||
RaiseCrash(sig);
|
||||
}
|
||||
|
||||
#endif /* __aarch64__ */
|
||||
|
|
|
@ -97,7 +97,7 @@ static void RemoveCrashHandler(void *arg) {
|
|||
strace_enabled(+1);
|
||||
}
|
||||
|
||||
static void InstallCrashHandler(int sig, sigaction_f thunk, int extraflags) {
|
||||
static void InstallCrashHandler(int sig, sigaction_f thunk) {
|
||||
int e;
|
||||
struct sigaction sa;
|
||||
struct CrashHandler *ch;
|
||||
|
@ -114,7 +114,7 @@ static void InstallCrashHandler(int sig, sigaction_f thunk, int extraflags) {
|
|||
sigdelset(&sa.sa_mask, SIGABRT);
|
||||
sigdelset(&sa.sa_mask, SIGBUS);
|
||||
sigdelset(&sa.sa_mask, SIGURG);
|
||||
sa.sa_flags = SA_SIGINFO | SA_NODEFER | extraflags;
|
||||
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
if (!sigaction(sig, &sa, &ch->old)) {
|
||||
__cxa_atexit(RemoveCrashHandler, ch, 0);
|
||||
}
|
||||
|
@ -138,24 +138,21 @@ static void InstallCrashHandler(int sig, sigaction_f thunk, int extraflags) {
|
|||
* useful, for example, if a program is caught in an infinite loop.
|
||||
*/
|
||||
void ShowCrashReports(void) {
|
||||
int ef = 0;
|
||||
struct sigaltstack ss;
|
||||
_wantcrashreports = true;
|
||||
if (!IsWindows()) {
|
||||
ef = SA_ONSTACK;
|
||||
struct sigaltstack ss;
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_size = GetStackSize();
|
||||
// FreeBSD sigaltstack() will EFAULT if we use MAP_STACK here
|
||||
// OpenBSD sigaltstack() auto-applies MAP_STACK to the memory
|
||||
npassert((ss.ss_sp = _mapanon(GetStackSize())));
|
||||
npassert(!sigaltstack(&ss, 0));
|
||||
ss.ss_size = SIGSTKSZ;
|
||||
ss.ss_sp = malloc(SIGSTKSZ);
|
||||
sigaltstack(&ss, 0);
|
||||
__cxa_atexit(free, ss.ss_sp, 0);
|
||||
}
|
||||
InstallCrashHandler(SIGQUIT, __got_sigquit, ef); // ctrl+\ aka ctrl+break
|
||||
InstallCrashHandler(SIGFPE, __got_sigfpe, ef); // 1 / 0
|
||||
InstallCrashHandler(SIGILL, __got_sigill, ef); // illegal instruction
|
||||
InstallCrashHandler(SIGSEGV, __got_sigsegv, ef); // bad memory access
|
||||
InstallCrashHandler(SIGTRAP, __got_sigtrap, ef); // bad system call
|
||||
InstallCrashHandler(SIGBUS, __got_sigbus, ef); // misalign, mmap i/o failed
|
||||
InstallCrashHandler(SIGURG, __got_sigurg, ef); // placeholder
|
||||
InstallCrashHandler(SIGQUIT, __got_sigquit); // ctrl+\ aka ctrl+break
|
||||
InstallCrashHandler(SIGFPE, __got_sigfpe); // 1 / 0
|
||||
InstallCrashHandler(SIGILL, __got_sigill); // illegal instruction
|
||||
InstallCrashHandler(SIGSEGV, __got_sigsegv); // bad memory access
|
||||
InstallCrashHandler(SIGTRAP, __got_sigtrap); // bad system call
|
||||
InstallCrashHandler(SIGBUS, __got_sigbus); // misalign, mmap i/o failed
|
||||
InstallCrashHandler(SIGURG, __got_sigurg); // placeholder
|
||||
_wantcrashreports = true;
|
||||
GetSymbolTable();
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/intrin/likely.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
|
|
@ -155,6 +155,11 @@ static char *Finish(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static int Pause(void) {
|
||||
pause();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int True(void) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -671,6 +676,7 @@ static int TryBuiltin(void) {
|
|||
if (!strcmp(args[0], "true")) return True();
|
||||
if (!strcmp(args[0], "test")) return Test();
|
||||
if (!strcmp(args[0], "kill")) return Kill();
|
||||
if (!strcmp(args[0], "pause")) return Pause();
|
||||
if (!strcmp(args[0], "flock")) return Flock();
|
||||
if (!strcmp(args[0], "chmod")) return Chmod();
|
||||
if (!strcmp(args[0], "touch")) return Touch();
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/utmp.h"
|
||||
|
@ -28,23 +30,37 @@
|
|||
/**
|
||||
* Prepares terminal for login.
|
||||
*
|
||||
* After this operation `fd` will be used for all stdio handles.
|
||||
*
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @raise EPERM if terminal is already controlling another sid?
|
||||
* @raise EPERM if pledge() was used without tty
|
||||
* @raise ENOTTY if `fd` isn't a teletypewriter
|
||||
* @raise ENOSYS on Windows and Metal
|
||||
* @raise EPERM if terminal is already controlling another sid
|
||||
* @raise EBADF if `fd` isn't open
|
||||
*/
|
||||
int login_tty(int fd) {
|
||||
int i, rc;
|
||||
if (IsLinux() || IsBsd()) {
|
||||
setsid();
|
||||
if (!sys_ioctl(fd, TIOCSCTTY, 0)) {
|
||||
for (i = 0; i < 3; ++i) dup2(fd, i);
|
||||
if (fd > 2) close(fd);
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
int rc;
|
||||
if (!IsLinux() && !IsBsd()) {
|
||||
rc = enosys();
|
||||
} else if (!isatty(fd)) {
|
||||
rc = -1; // validate before changing the process's state
|
||||
} else {
|
||||
// become session leader
|
||||
// we don't care if it fails due to already being the one
|
||||
int e = errno;
|
||||
sys_setsid();
|
||||
errno = e;
|
||||
// take control of teletypewriter (requires being leader)
|
||||
if ((rc = sys_ioctl(fd, TIOCSCTTY, 0)) != -1) {
|
||||
unassert(!sys_dup2(fd, 0, 0));
|
||||
unassert(!sys_dup2(fd, 1, 0));
|
||||
unassert(!sys_dup2(fd, 2, 0));
|
||||
if (fd > 2) {
|
||||
unassert(!sys_close(fd));
|
||||
}
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
STRACE("login_tty(%d) → %d% m", fd, rc);
|
||||
return rc;
|
||||
|
|
|
@ -76,6 +76,7 @@ o/$(MODE)/libc/runtime/ftracer.o: private \
|
|||
|
||||
o/$(MODE)/libc/runtime/cosmo2.o \
|
||||
o/$(MODE)/libc/runtime/fork-nt.o \
|
||||
o/$(MODE)/libc/runtime/enable_tls.o \
|
||||
o/$(MODE)/libc/runtime/printmemoryintervals.o \
|
||||
o/$(MODE)/libc/runtime/findmemoryinterval.o \
|
||||
o/$(MODE)/libc/runtime/sys_mprotect.greg.o \
|
||||
|
@ -92,6 +93,7 @@ o/$(MODE)/libc/runtime/print.greg.o \
|
|||
o/$(MODE)/libc/runtime/stackchkfail.o \
|
||||
o/$(MODE)/libc/runtime/stackchkfaillocal.o \
|
||||
o/$(MODE)/libc/runtime/winmain.greg.o \
|
||||
o/$(MODE)/libc/runtime/interceptflag.greg.o \
|
||||
o/$(MODE)/libc/runtime/opensymboltable.o: private \
|
||||
CFLAGS += \
|
||||
-Os \
|
||||
|
|
|
@ -60,7 +60,8 @@ static textexit void LogStackUse(void) {
|
|||
if (!PLEDGED(STDIO) || !PLEDGED(WPATH) || !PLEDGED(CPATH)) return;
|
||||
usage = GetStackUsage((char *)GetStackAddr(), GetStackSize());
|
||||
e = errno;
|
||||
if ((fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY, 0644)) != -1) {
|
||||
if ((fd = open(stacklog, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0644)) !=
|
||||
-1) {
|
||||
p = FormatUint64(stacklog, usage);
|
||||
for (i = 0; i < __argc; ++i) {
|
||||
n = strlen(__argv[i]);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/asancodes.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
@ -176,6 +178,27 @@ __msabi static textwindows wontreturn void WinInit(const char16_t *cmdline) {
|
|||
struct WinArgs *wa =
|
||||
(struct WinArgs *)(stackaddr + (stacksize - sizeof(struct WinArgs)));
|
||||
|
||||
// allocate asan memory if needed
|
||||
if (IsAsan()) {
|
||||
uintptr_t shadowaddr = 0x7fff8000 + (stackaddr >> 3);
|
||||
uintptr_t shadowend = 0x7fff8000 + ((stackaddr + stacksize) >> 3);
|
||||
uintptr_t shallocaddr = ROUNDDOWN(shadowaddr, FRAMESIZE);
|
||||
uintptr_t shallocend = ROUNDUP(shadowend, FRAMESIZE);
|
||||
uintptr_t shallocsize = shallocend - shallocaddr;
|
||||
__imp_MapViewOfFileEx(
|
||||
(_mmi.p[1].h =
|
||||
__imp_CreateFileMappingW(-1, &kNtIsInheritable, kNtPageReadwrite,
|
||||
shallocsize >> 32, shallocsize, NULL)),
|
||||
kNtFileMapWrite, 0, 0, shallocsize, (void *)shallocaddr);
|
||||
_mmi.p[1].x = shallocaddr >> 16;
|
||||
_mmi.p[1].y = (shallocaddr >> 16) + ((shallocsize - 1) >> 16);
|
||||
_mmi.p[1].prot = PROT_READ | PROT_WRITE;
|
||||
_mmi.p[1].flags = 0x00000022; // private+anonymous
|
||||
_mmi.p[1].size = shallocsize;
|
||||
_mmi.i = 2;
|
||||
__asan_poison((void *)stackaddr, GetGuardSize(), kAsanStackOverflow);
|
||||
}
|
||||
|
||||
// parse utf-16 command into utf-8 argv array in argument block
|
||||
int count = GetDosArgv(cmdline, wa->argblock, ARRAYLEN(wa->argblock),
|
||||
wa->argv, ARRAYLEN(wa->argv));
|
||||
|
@ -231,13 +254,13 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
|||
// sloppy flag-only check for early initialization
|
||||
if (__strstr16(cmdline, u"--strace")) ++__strace;
|
||||
#endif
|
||||
if (_weaken(WinSockInit)) {
|
||||
_weaken(WinSockInit)();
|
||||
}
|
||||
DeduplicateStdioHandles();
|
||||
if (_weaken(WinMainStdin)) {
|
||||
_weaken(WinMainStdin)();
|
||||
}
|
||||
if (_weaken(WinSockInit)) {
|
||||
_weaken(WinSockInit)();
|
||||
}
|
||||
if (_weaken(WinMainForked)) {
|
||||
_weaken(WinMainForked)();
|
||||
}
|
||||
|
|
|
@ -16,13 +16,16 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
TEST(close, stdout) {
|
||||
if (!IsWindows()) return;
|
||||
close(1);
|
||||
fprintf(stderr, "hi\n");
|
||||
size_t confstr(int name, char *buf, size_t len) {
|
||||
if (name == _CS_PATH) {
|
||||
return strlcpy(buf, _PATH_DEFPATH, len) + 1;
|
||||
} else {
|
||||
einval();
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/fflush.internal.h"
|
||||
#include "libc/stdio/internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
|
@ -42,6 +43,7 @@
|
|||
* @param mode can be:
|
||||
* - `"r"` for reading from subprocess standard output
|
||||
* - `"w"` for writing to subprocess standard input
|
||||
* - `"e"` for `O_CLOEXEC` on returned file
|
||||
* @raise EINVAL if `mode` is invalid or specifies read+write
|
||||
* @raise EMFILE if process `RLIMIT_NOFILE` has been reached
|
||||
* @raise ENFILE if system-wide file limit has been reached
|
||||
|
@ -53,7 +55,7 @@
|
|||
* @threadsafe
|
||||
*/
|
||||
FILE *popen(const char *cmdline, const char *mode) {
|
||||
FILE *f;
|
||||
FILE *f, *f2;
|
||||
int e, rc, pid, dir, flags, pipefds[2];
|
||||
flags = fopenflags(mode);
|
||||
if ((flags & O_ACCMODE) == O_RDONLY) {
|
||||
|
@ -77,6 +79,14 @@ FILE *popen(const char *cmdline, const char *mode) {
|
|||
// we can't rely on cloexec because cocmd builtins don't execve
|
||||
if (pipefds[0] != !dir) unassert(!close(pipefds[0]));
|
||||
if (pipefds[1] != !dir) unassert(!close(pipefds[1]));
|
||||
// "The popen() function shall ensure that any streams from
|
||||
// previous popen() calls that remain open in the parent
|
||||
// process are closed in the new child process." -POSIX
|
||||
for (int i = 0; i < __fflush.handles.i; ++i) {
|
||||
if ((f2 = __fflush.handles.p[i]) && f2->pid) {
|
||||
close(f2->fd);
|
||||
}
|
||||
}
|
||||
_Exit(_cocmd(3,
|
||||
(char *[]){
|
||||
"popen",
|
||||
|
@ -88,18 +98,21 @@ FILE *popen(const char *cmdline, const char *mode) {
|
|||
default:
|
||||
f->pid = pid;
|
||||
unassert(!close(pipefds[!dir]));
|
||||
if (!(flags & O_CLOEXEC)) {
|
||||
unassert(!fcntl(pipefds[dir], F_SETFD, 0));
|
||||
}
|
||||
return f;
|
||||
case -1:
|
||||
e = errno;
|
||||
unassert(!fclose(f));
|
||||
unassert(!close(pipefds[!dir]));
|
||||
fclose(f);
|
||||
close(pipefds[!dir]);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
e = errno;
|
||||
unassert(!close(pipefds[0]));
|
||||
unassert(!close(pipefds[1]));
|
||||
close(pipefds[0]);
|
||||
close(pipefds[1]);
|
||||
errno = e;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -21,9 +21,13 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/ntspawn.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
|
@ -42,8 +46,11 @@
|
|||
#include "libc/sock/sock.h"
|
||||
#include "libc/stdio/posix_spawn.h"
|
||||
#include "libc/stdio/posix_spawn.internal.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/limits.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
@ -149,13 +156,14 @@ static textwindows errno_t posix_spawn_windows_impl(
|
|||
};
|
||||
|
||||
// figure out the flags
|
||||
short flags;
|
||||
short flags = 0;
|
||||
bool bInheritHandles = false;
|
||||
uint32_t dwCreationFlags = 0;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
bInheritHandles |= stdio_handle[i] != -1;
|
||||
}
|
||||
if (attrp && *attrp && !posix_spawnattr_getflags(attrp, &flags)) {
|
||||
if (attrp && *attrp) {
|
||||
flags = (*attrp)->flags;
|
||||
if (flags & POSIX_SPAWN_SETSID) {
|
||||
dwCreationFlags |= kNtDetachedProcess;
|
||||
}
|
||||
|
@ -208,8 +216,11 @@ static textwindows dontinline errno_t posix_spawn_windows(
|
|||
return err;
|
||||
}
|
||||
|
||||
static wontreturn void posix_spawn_die(const char *fail_func) {
|
||||
STRACE("posix_spawn: %s failed% m", fail_func);
|
||||
static wontreturn void posix_spawn_die(const char *thing) {
|
||||
const char *reason; // print b/c there's no other way
|
||||
if (!(reason = _strerdoc(errno))) reason = "Unknown error";
|
||||
tinyprint(2, program_invocation_short_name, ": posix_spawn ", thing,
|
||||
" failed: ", reason, "\n", NULL);
|
||||
_Exit(127);
|
||||
}
|
||||
|
||||
|
@ -229,7 +240,7 @@ static void RunUnixFileActions(struct _posix_faction *a) {
|
|||
case _POSIX_SPAWN_OPEN: {
|
||||
int t;
|
||||
if ((t = open(a->path, a->oflag, a->mode)) == -1) {
|
||||
posix_spawn_die("open");
|
||||
posix_spawn_die(a->path);
|
||||
}
|
||||
if (t != a->fildes) {
|
||||
if (dup2(t, a->fildes) == -1) {
|
||||
|
@ -251,8 +262,7 @@ static void RunUnixFileActions(struct _posix_faction *a) {
|
|||
/**
|
||||
* Spawns process, the POSIX way.
|
||||
*
|
||||
* This provides superior process creation performance across systems.
|
||||
*
|
||||
* This provides superior process creation performance across systems
|
||||
* Processes are normally spawned by calling fork() and execve(), but
|
||||
* that goes slow on Windows if the caller has allocated a nontrivial
|
||||
* number of memory mappings, all of which need to be copied into the
|
||||
|
@ -260,10 +270,19 @@ static void RunUnixFileActions(struct _posix_faction *a) {
|
|||
* fork() bears a similar cost that's 100x less bad, which is copying
|
||||
* the page tables. So what this implementation does is on Windows it
|
||||
* calls CreateProcess() directly and on UNIX it uses vfork() if it's
|
||||
* possible (XNU and OpenBSD don't have it).
|
||||
* possible (XNU and OpenBSD don't have it). On UNIX this API has the
|
||||
* benefit of avoiding the footguns of using vfork() directly because
|
||||
* this implementation will ensure signal handlers can't be called in
|
||||
* the child process since that'd likely corrupt the parent's memory.
|
||||
*
|
||||
* If the child process exits with status 127 then use the `--strace`
|
||||
* flag to get an explanation of failures that occurred during spawn.
|
||||
* This implementation doesn't create a pipe like Musl Libc, and will
|
||||
* report errors in the child process by printing the cause to stderr
|
||||
* which ensures posix_spawn stays fast, and works w/ `RLIMIT_FILENO`
|
||||
* There is however one particular case where knowing the execve code
|
||||
* truly matters, which is ETXTBSY. Thankfully, there's a rarely used
|
||||
* signal with the exact same magic number across supported platforms
|
||||
* called SIGVTALRM which we send to wait4 when execve raises ETXTBSY
|
||||
* Please note that on Windows posix_spawn() returns ETXTBSY directly
|
||||
*
|
||||
* @param pid if non-null shall be set to child pid on success
|
||||
* @param path is resolved path of program which is not `$PATH` searched
|
||||
|
@ -280,67 +299,85 @@ errno_t posix_spawn(int *pid, const char *path,
|
|||
const posix_spawn_file_actions_t *file_actions,
|
||||
const posix_spawnattr_t *attrp, char *const argv[],
|
||||
char *const envp[]) {
|
||||
short flags = 0;
|
||||
sigset_t sigmask;
|
||||
int s, child, policy;
|
||||
struct sched_param param;
|
||||
struct sigaction dfl = {0};
|
||||
if (IsWindows()) {
|
||||
return posix_spawn_windows(pid, path, file_actions, attrp, argv, envp);
|
||||
}
|
||||
sigset_t blockall, oldmask;
|
||||
int child, res, cs, e = errno;
|
||||
if (access(path, X_OK)) {
|
||||
res = errno;
|
||||
errno = e;
|
||||
return res;
|
||||
}
|
||||
sigfillset(&blockall);
|
||||
sys_sigprocmask(SIG_SETMASK, &blockall, &oldmask);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
|
||||
if (!(child = vfork())) {
|
||||
if (attrp && *attrp) {
|
||||
posix_spawnattr_getflags(attrp, &flags);
|
||||
if (flags & POSIX_SPAWN_SETSID) {
|
||||
if (setsid()) {
|
||||
posix_spawn_die("setsid");
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETPGROUP) {
|
||||
if (setpgid(0, (*attrp)->pgroup)) {
|
||||
posix_spawn_die("setpgid");
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSIGMASK) {
|
||||
posix_spawnattr_getsigmask(attrp, &sigmask);
|
||||
sigprocmask(SIG_SETMASK, &sigmask, 0);
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_RESETIDS) &&
|
||||
(setgid(getgid()) || setuid(getuid()))) {
|
||||
posix_spawn_die("setuid");
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSIGDEF) {
|
||||
for (s = 1; s < 32; s++) {
|
||||
if (sigismember(&(*attrp)->sigdefault, s)) {
|
||||
if (sigaction(s, &dfl, 0)) {
|
||||
posix_spawn_die("sigaction");
|
||||
}
|
||||
}
|
||||
}
|
||||
sigset_t *childmask;
|
||||
struct sigaction dfl = {0};
|
||||
short flags = attrp && *attrp ? (*attrp)->flags : 0;
|
||||
for (int sig = 1; sig < _NSIG; sig++) {
|
||||
if (__sighandrvas[sig] != (long)SIG_DFL &&
|
||||
(__sighandrvas[sig] != (long)SIG_IGN ||
|
||||
((flags & POSIX_SPAWN_SETSIGDEF) &&
|
||||
sigismember(&(*attrp)->sigdefault, sig) == 1) ||
|
||||
sig == SIGVTALRM)) {
|
||||
sigaction(sig, &dfl, 0);
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSIGMASK) {
|
||||
childmask = &(*attrp)->sigmask;
|
||||
} else {
|
||||
childmask = &oldmask;
|
||||
}
|
||||
sys_sigprocmask(SIG_SETMASK, childmask, 0);
|
||||
if (flags & POSIX_SPAWN_SETSID) {
|
||||
setsid();
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_SETPGROUP) && setpgid(0, (*attrp)->pgroup)) {
|
||||
posix_spawn_die("setpgid");
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_RESETIDS) && setgid(getgid())) {
|
||||
posix_spawn_die("setgid");
|
||||
}
|
||||
if ((flags & POSIX_SPAWN_RESETIDS) && setgid(getgid())) {
|
||||
posix_spawn_die("setuid");
|
||||
}
|
||||
if (file_actions) {
|
||||
RunUnixFileActions(*file_actions);
|
||||
}
|
||||
if (attrp && *attrp) {
|
||||
if (IsLinux() || IsFreebsd() || IsNetbsd()) {
|
||||
if (flags & POSIX_SPAWN_SETSCHEDULER) {
|
||||
posix_spawnattr_getschedpolicy(attrp, &policy);
|
||||
posix_spawnattr_getschedparam(attrp, ¶m);
|
||||
if (sched_setscheduler(0, policy, ¶m) == -1) {
|
||||
if (sched_setscheduler(0, (*attrp)->schedpolicy,
|
||||
&(*attrp)->schedparam) == -1) {
|
||||
posix_spawn_die("sched_setscheduler");
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETSCHEDPARAM) {
|
||||
posix_spawnattr_getschedparam(attrp, ¶m);
|
||||
if (sched_setparam(0, ¶m)) {
|
||||
if (sched_setparam(0, &(*attrp)->schedparam)) {
|
||||
posix_spawn_die("sched_setparam");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flags & POSIX_SPAWN_SETRLIMIT) {
|
||||
for (int rez = 0; rez <= ARRAYLEN((*attrp)->rlim); ++rez) {
|
||||
if ((*attrp)->rlimset & (1u << rez)) {
|
||||
if (setrlimit(rez, (*attrp)->rlim + rez)) {
|
||||
posix_spawn_die("setrlimit");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!envp) envp = environ;
|
||||
execve(path, argv, envp);
|
||||
posix_spawn_die("execve");
|
||||
} else if (child != -1) {
|
||||
if (errno == ETXTBSY) {
|
||||
sys_kill(getpid(), SIGVTALRM, 1);
|
||||
}
|
||||
posix_spawn_die(path);
|
||||
}
|
||||
sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
|
||||
pthread_setcancelstate(cs, 0);
|
||||
if (child != -1) {
|
||||
if (pid) *pid = child;
|
||||
return 0;
|
||||
} else {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWN_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_SPAWN_H_
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
||||
#define POSIX_SPAWN_USEVFORK 0
|
||||
#define POSIX_SPAWN_RESETIDS 1
|
||||
#define POSIX_SPAWN_SETPGROUP 2
|
||||
#define POSIX_SPAWN_SETSIGDEF 4
|
||||
|
@ -10,6 +12,7 @@
|
|||
#define POSIX_SPAWN_SETSCHEDPARAM 16
|
||||
#define POSIX_SPAWN_SETSCHEDULER 32
|
||||
#define POSIX_SPAWN_SETSID 128
|
||||
#define POSIX_SPAWN_SETRLIMIT 256
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
@ -45,6 +48,8 @@ int posix_spawnattr_getsigmask(const posix_spawnattr_t *, sigset_t *);
|
|||
int posix_spawnattr_setsigmask(posix_spawnattr_t *, const sigset_t *);
|
||||
int posix_spawnattr_getsigdefault(const posix_spawnattr_t *, sigset_t *);
|
||||
int posix_spawnattr_setsigdefault(posix_spawnattr_t *, const sigset_t *);
|
||||
int posix_spawnattr_getrlimit(const posix_spawnattr_t *, int, struct rlimit *);
|
||||
int posix_spawnattr_setrlimit(posix_spawnattr_t *, int, const struct rlimit *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_SPAWNA_INTERNAL_H_
|
||||
#include "libc/calls/struct/rlimit.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
|
||||
|
@ -11,15 +12,16 @@
|
|||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct _posix_spawna {
|
||||
char flags;
|
||||
short flags;
|
||||
bool schedparam_isset;
|
||||
bool schedpolicy_isset;
|
||||
bool sigmask_isset;
|
||||
int pgroup;
|
||||
int rlimset;
|
||||
int schedpolicy;
|
||||
struct sched_param schedparam;
|
||||
sigset_t sigmask;
|
||||
sigset_t sigdefault;
|
||||
struct rlimit rlim[16];
|
||||
};
|
||||
|
||||
struct _posix_faction {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/posix_spawn.h"
|
||||
#include "libc/stdio/posix_spawn.internal.h"
|
||||
|
@ -76,11 +77,6 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
|
|||
/**
|
||||
* Sets posix_spawn() flags.
|
||||
*
|
||||
* Setting these flags is needed in order for the other setters in this
|
||||
* function to take effect. If a flag is known but unsupported by the
|
||||
* host platform, it'll be silently removed from the flags. You can
|
||||
* check for this by calling the getter afterwards.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param flags may have any of the following
|
||||
* - `POSIX_SPAWN_RESETIDS`
|
||||
|
@ -94,9 +90,6 @@ int posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) {
|
|||
* @raise EINVAL if `flags` has invalid bits
|
||||
*/
|
||||
int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
||||
if (!(IsLinux() || IsFreebsd() || IsNetbsd())) {
|
||||
flags &= ~(POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER);
|
||||
}
|
||||
if (flags &
|
||||
~(POSIX_SPAWN_RESETIDS | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF |
|
||||
POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSCHEDPARAM |
|
||||
|
@ -107,134 +100,62 @@ int posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets process group id associated with attributes.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param pgroup receives the result on success
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getpgroup(const posix_spawnattr_t *attr, int *pgroup) {
|
||||
*pgroup = (*attr)->pgroup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies process group into which child process is placed.
|
||||
*
|
||||
* Setting `pgroup` to zero will ensure newly created processes are
|
||||
* placed within their own brand new process group.
|
||||
*
|
||||
* This setter also sets the `POSIX_SPAWN_SETPGROUP` flag.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param pgroup is the process group id, or 0 for self
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, int pgroup) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETPGROUP;
|
||||
(*attr)->pgroup = pgroup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets scheduler policy that'll be used for spawned process.
|
||||
*
|
||||
* If the setter wasn't called then this function will return the
|
||||
* scheduling policy of the current process.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedpolicy receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOSYS if platform support isn't available
|
||||
*/
|
||||
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
|
||||
int *schedpolicy) {
|
||||
int rc, e = errno;
|
||||
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
|
||||
if (!a->schedpolicy_isset) {
|
||||
rc = sched_getscheduler(0);
|
||||
if (rc == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
a->schedpolicy = rc;
|
||||
a->schedpolicy_isset = true;
|
||||
}
|
||||
*schedpolicy = a->schedpolicy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies scheduler policy override for spawned process.
|
||||
*
|
||||
* Scheduling policies are inherited by default. Use this to change it.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedpolicy receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
|
||||
(*attr)->schedpolicy = schedpolicy;
|
||||
(*attr)->schedpolicy_isset = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets scheduler parameter that'll be used for spawned process.
|
||||
*
|
||||
* If the setter wasn't called then this function will return the
|
||||
* scheduling parameter of the current process.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise ENOSYS if platform support isn't available
|
||||
*/
|
||||
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
|
||||
struct sched_param *schedparam) {
|
||||
int rc, e = errno;
|
||||
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
|
||||
if (!a->schedparam_isset) {
|
||||
rc = sched_getparam(0, &a->schedparam);
|
||||
if (rc == -1) {
|
||||
rc = errno;
|
||||
errno = e;
|
||||
return rc;
|
||||
}
|
||||
a->schedparam_isset = true;
|
||||
}
|
||||
*schedparam = a->schedparam;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies scheduler parameter override for spawned process.
|
||||
*
|
||||
* Scheduling parameters are inherited by default. Use this to change it.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
||||
const struct sched_param *schedparam) {
|
||||
(*attr)->schedparam = *schedparam;
|
||||
(*attr)->schedparam_isset = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets signal mask for sigprocmask() in child process.
|
||||
*
|
||||
* If the setter wasn't called then this function will return the
|
||||
* scheduling parameter of the current process.
|
||||
* The signal mask is applied to the child process in such a way that
|
||||
* signal handlers from the parent process can't get triggered in the
|
||||
* child process.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getsigmask(const posix_spawnattr_t *attr,
|
||||
sigset_t *sigmask) {
|
||||
struct _posix_spawna *a = *(/*unconst*/ posix_spawnattr_t *)attr;
|
||||
if (!a->sigmask_isset) {
|
||||
npassert(!sigprocmask(SIG_SETMASK, 0, &a->sigmask));
|
||||
a->sigmask_isset = true;
|
||||
}
|
||||
*sigmask = a->sigmask;
|
||||
*sigmask = (*attr)->sigmask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies signal mask for sigprocmask() in child process.
|
||||
*
|
||||
* Signal masks are inherited by default. Use this to change it.
|
||||
* This setter also sets the `POSIX_SPAWN_SETSIGMASK` flag.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setsigmask(posix_spawnattr_t *attr,
|
||||
const sigset_t *sigmask) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSIGMASK;
|
||||
(*attr)->sigmask = *sigmask;
|
||||
(*attr)->sigmask_isset = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -252,10 +173,119 @@ int posix_spawnattr_getsigdefault(const posix_spawnattr_t *attr,
|
|||
/**
|
||||
* Specifies which signals should be restored to `SIG_DFL`.
|
||||
*
|
||||
* This routine isn't necessary in most cases, since posix_spawn() by
|
||||
* default will try to avoid vfork() race conditions by tracking what
|
||||
* signals have a handler function and then resets them automatically
|
||||
* within the child process, before applying the child's signal mask.
|
||||
* This function may be used to ensure the `SIG_IGN` disposition will
|
||||
* not propagate across execve in cases where this process explicitly
|
||||
* set the signals to `SIG_IGN` earlier (since posix_spawn() will not
|
||||
* issue O(128) system calls just to be totally pedantic about that).
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSIGDEF`.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setsigdefault(posix_spawnattr_t *attr,
|
||||
const sigset_t *sigdefault) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSIGDEF;
|
||||
(*attr)->sigdefault = *sigdefault;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets resource limit for spawned process.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if `resource` is invalid
|
||||
* @raise ENOENT if `resource` is absent
|
||||
*/
|
||||
int posix_spawnattr_getrlimit(const posix_spawnattr_t *attr, int resource,
|
||||
struct rlimit *rlim) {
|
||||
if ((0 <= resource && resource < ARRAYLEN((*attr)->rlim))) {
|
||||
if (((*attr)->rlimset & (1u << resource))) {
|
||||
*rlim = (*attr)->rlim[resource];
|
||||
return 0;
|
||||
} else {
|
||||
return ENOENT;
|
||||
}
|
||||
} else {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets resource limit on spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETRLIMIT`.
|
||||
*
|
||||
* @return 0 on success, or errno on error
|
||||
* @raise EINVAL if resource is invalid
|
||||
*/
|
||||
int posix_spawnattr_setrlimit(posix_spawnattr_t *attr, int resource,
|
||||
const struct rlimit *rlim) {
|
||||
if (0 <= resource && resource < ARRAYLEN((*attr)->rlim)) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETRLIMIT;
|
||||
(*attr)->rlimset |= 1u << resource;
|
||||
(*attr)->rlim[resource] = *rlim;
|
||||
return 0;
|
||||
} else {
|
||||
return EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets scheduler policy that'll be used for spawned process.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedpolicy receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getschedpolicy(const posix_spawnattr_t *attr,
|
||||
int *schedpolicy) {
|
||||
*schedpolicy = (*attr)->schedpolicy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies scheduler policy override for spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDULER`.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedpolicy(posix_spawnattr_t *attr, int schedpolicy) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDULER;
|
||||
(*attr)->schedpolicy = schedpolicy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets scheduler parameter.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_getschedparam(const posix_spawnattr_t *attr,
|
||||
struct sched_param *schedparam) {
|
||||
*schedparam = (*attr)->schedparam;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies scheduler parameter override for spawned process.
|
||||
*
|
||||
* Using this setter automatically sets `POSIX_SPAWN_SETSCHEDPARAM`.
|
||||
*
|
||||
* @param attr was initialized by posix_spawnattr_init()
|
||||
* @param schedparam receives the result
|
||||
* @return 0 on success, or errno on error
|
||||
*/
|
||||
int posix_spawnattr_setschedparam(posix_spawnattr_t *attr,
|
||||
const struct sched_param *schedparam) {
|
||||
(*attr)->flags |= POSIX_SPAWN_SETSCHEDPARAM;
|
||||
(*attr)->schedparam = *schedparam;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_STDIO_H_
|
||||
#define COSMOPOLITAN_LIBC_STDIO_H_
|
||||
|
||||
#define EOF -1 /* end of file */
|
||||
#define WEOF -1u /* end of file (multibyte) */
|
||||
#define _IOFBF 0 /* fully buffered */
|
||||
#define _IOLBF 1 /* line buffered */
|
||||
#define _IONBF 2 /* no buffering */
|
||||
#define EOF -1 /* end of file */
|
||||
#define WEOF -1u /* end of file (multibyte) */
|
||||
#define _IOFBF 0 /* fully buffered */
|
||||
#define _IOLBF 1 /* line buffered */
|
||||
#define _IONBF 2 /* no buffering */
|
||||
#define _CS_PATH 0
|
||||
|
||||
#define L_tmpnam 20
|
||||
#define L_ctermid 20
|
||||
|
@ -80,6 +81,7 @@ int setvbuf(FILE *, char *, int, size_t);
|
|||
int pclose(FILE *);
|
||||
char *ctermid(char *);
|
||||
void perror(const char *) relegated;
|
||||
size_t confstr(int, char *, size_t);
|
||||
|
||||
typedef uint64_t fpos_t;
|
||||
char *gets(char *) paramsnonnull();
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
* This creates a secure temporary file inside $TMPDIR. If it isn't
|
||||
* defined, then /tmp is used on UNIX and GetTempPath() is used on the
|
||||
* New Technology. This resolution of $TMPDIR happens once in a ctor,
|
||||
* which is copied to the `kTmpDir` global.
|
||||
* which is copied to the `kTmpPath` global.
|
||||
*
|
||||
* Once fclose() is called, the returned file is guaranteed to be
|
||||
* deleted automatically. On UNIX the file is unlink()'d before this
|
||||
|
|
|
@ -62,8 +62,8 @@ axdx_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) {
|
|||
r.ax = 0;
|
||||
r.dx = 0;
|
||||
for (;;) {
|
||||
#ifdef __x86_64__
|
||||
if (!IsTiny() && !((uintptr_t)(src + r.dx) & 15)) {
|
||||
#if defined(__x86_64__) && !IsModeDbg()
|
||||
if (!((uintptr_t)(src + r.dx) & 15)) {
|
||||
r = tprecode8to16_sse2(dst, dstsize, src, r);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
char *mktemp(char *) returnsnonnull paramsnonnull() __wur;
|
||||
char *mktemp(char *) returnsnonnull paramsnonnull();
|
||||
char *mkdtemp(char *) paramsnonnull() __wur;
|
||||
int mkstemp(char *) paramsnonnull() __wur;
|
||||
int mkstemps(char *, int) paramsnonnull() __wur;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_THREAD_POSIXTHREAD_INTERNAL_H_
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigaltstack.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -75,7 +76,6 @@ struct PosixThread {
|
|||
void *(*start)(void *); // creation callback
|
||||
void *arg; // start's parameter
|
||||
void *rc; // start's return value
|
||||
char *altstack; // thread sigaltstack
|
||||
char *tls; // bottom of tls allocation
|
||||
struct CosmoTib *tib; // middle of tls allocation
|
||||
struct Dll list; // list of threads
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "libc/atomic.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigaltstack.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
|
@ -29,7 +28,7 @@
|
|||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/bsr.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/popcnt.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -60,64 +59,30 @@ static unsigned long roundup2pow(unsigned long x) {
|
|||
}
|
||||
|
||||
void _pthread_free(struct PosixThread *pt) {
|
||||
static atomic_uint freed;
|
||||
if (pt->flags & PT_STATIC) return;
|
||||
free(pt->tls);
|
||||
if ((pt->flags & PT_OWNSTACK) && //
|
||||
pt->attr.__stackaddr && //
|
||||
pt->attr.__stackaddr != MAP_FAILED) {
|
||||
npassert(!munmap(pt->attr.__stackaddr, pt->attr.__stacksize));
|
||||
}
|
||||
if (pt->altstack) {
|
||||
free(pt->altstack);
|
||||
unassert(!munmap(pt->attr.__stackaddr, pt->attr.__stacksize));
|
||||
}
|
||||
free(pt);
|
||||
if (popcnt(atomic_fetch_add_explicit(&freed, 1, memory_order_acq_rel)) == 1) {
|
||||
malloc_trim(0);
|
||||
}
|
||||
}
|
||||
|
||||
void pthread_kill_siblings_np(void) {
|
||||
struct Dll *e, *e2;
|
||||
struct PosixThread *pt, *self;
|
||||
self = (struct PosixThread *)__get_tls()->tib_pthread;
|
||||
pthread_spin_lock(&_pthread_lock);
|
||||
for (e = dll_first(_pthread_list); e; e = e2) {
|
||||
e2 = dll_next(_pthread_list, e);
|
||||
pt = POSIXTHREAD_CONTAINER(e);
|
||||
if (pt != self) {
|
||||
pthread_kill((pthread_t)pt, SIGKILL);
|
||||
dll_remove(&_pthread_list, e);
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
_pthread_free(pt);
|
||||
}
|
||||
}
|
||||
pthread_spin_unlock(&_pthread_lock);
|
||||
}
|
||||
|
||||
static int PosixThread(void *arg, int tid) {
|
||||
void *rc;
|
||||
struct sigaltstack ss;
|
||||
struct PosixThread *pt = arg;
|
||||
unassert(__get_tls()->tib_tid > 0);
|
||||
if (pt->altstack) {
|
||||
ss.ss_flags = 0;
|
||||
ss.ss_size = SIGSTKSZ;
|
||||
ss.ss_sp = pt->altstack;
|
||||
if (sigaltstack(&ss, 0)) {
|
||||
notpossible;
|
||||
}
|
||||
}
|
||||
if (pt->attr.__inheritsched == PTHREAD_EXPLICIT_SCHED) {
|
||||
_pthread_reschedule(pt);
|
||||
}
|
||||
// set long jump handler so pthread_exit can bring control back here
|
||||
if (!setjmp(pt->exiter)) {
|
||||
__get_tls()->tib_pthread = (pthread_t)pt;
|
||||
npassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
|
||||
unassert(!sigprocmask(SIG_SETMASK, (sigset_t *)pt->attr.__sigmask, 0));
|
||||
rc = pt->start(pt->arg);
|
||||
// ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup
|
||||
npassert(!pt->cleanup);
|
||||
unassert(!pt->cleanup);
|
||||
// calling pthread_exit() will either jump back here, or call exit
|
||||
pthread_exit(rc);
|
||||
}
|
||||
|
@ -251,11 +216,6 @@ static errno_t pthread_create_impl(pthread_t *thread,
|
|||
}
|
||||
}
|
||||
|
||||
// setup signal handler stack
|
||||
if (_wantcrashreports && !IsWindows()) {
|
||||
pt->altstack = malloc(SIGSTKSZ);
|
||||
}
|
||||
|
||||
// set initial status
|
||||
if (!pt->attr.__havesigmask) {
|
||||
pt->attr.__havesigmask = true;
|
||||
|
|
|
@ -194,7 +194,6 @@ int pthread_spin_unlock(pthread_spinlock_t *) paramsnonnull();
|
|||
int pthread_testcancel_np(void);
|
||||
int pthread_tryjoin_np(pthread_t, void **);
|
||||
int pthread_yield(void);
|
||||
void pthread_kill_siblings_np(void);
|
||||
pthread_id_np_t pthread_getthreadid_np(void);
|
||||
pthread_t pthread_self(void) pureconst;
|
||||
void *pthread_getspecific(pthread_key_t);
|
||||
|
|
|
@ -31,6 +31,7 @@ NET_HTTPS_A_DIRECTDEPS = \
|
|||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_X \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
void InitializeRng(mbedtls_ctr_drbg_context *r) {
|
||||
unsigned char b[64];
|
||||
mbedtls_ctr_drbg_init(r);
|
||||
CHECK(getrandom(b, 64, 0) == 64);
|
||||
CHECK(!mbedtls_ctr_drbg_seed(r, GetEntropy, 0, b, 64));
|
||||
getrandom(b, 64, 0);
|
||||
mbedtls_ctr_drbg_seed(r, GetEntropy, 0, b, 64);
|
||||
mbedtls_platform_zeroize(b, 64);
|
||||
}
|
||||
|
|
|
@ -1,220 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 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 "net/https/sslcache.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/bsr.h"
|
||||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
#include "third_party/mbedtls/x509_crt.h"
|
||||
|
||||
#define PROT (PROT_READ | PROT_WRITE)
|
||||
#define FLAGS MAP_SHARED
|
||||
|
||||
static uint32_t HashSslSession(const mbedtls_ssl_session *session) {
|
||||
int i;
|
||||
uint32_t h;
|
||||
h = session->ciphersuite;
|
||||
h *= 0x9e3779b1;
|
||||
h = session->compression;
|
||||
h *= 0x9e3779b1;
|
||||
for (i = 0; i < session->id_len; i++) {
|
||||
h += session->id[i];
|
||||
h *= 0x9e3779b1;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
static struct SslCache *OpenSslCache(const char *path, size_t size) {
|
||||
int fd;
|
||||
struct stat st;
|
||||
struct SslCache *c = NULL;
|
||||
if (path) {
|
||||
if ((fd = open(path, O_RDWR | O_CREAT, 0600)) != -1) {
|
||||
CHECK_NE(-1, fstat(fd, &st));
|
||||
if (st.st_size && st.st_size != size) {
|
||||
WARNF("unlinking sslcache because size changed from %,zu to %,zu",
|
||||
st.st_size, size);
|
||||
unlink(path);
|
||||
fd = open(path, O_RDWR | O_CREAT, 0600);
|
||||
st.st_size = 0;
|
||||
}
|
||||
if (fd != -1) {
|
||||
if (!st.st_size) CHECK_NE(-1, ftruncate(fd, size));
|
||||
c = mmap(0, size, PROT, FLAGS, fd, 0);
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
WARNF("sslcache open(%`'s) failed %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static unsigned long rounddown2pow(unsigned long x) {
|
||||
return x ? 1ul << _bsrl(x) : 0;
|
||||
}
|
||||
|
||||
struct SslCache *CreateSslCache(const char *path, size_t bytes, int lifetime) {
|
||||
size_t ents, size;
|
||||
struct SslCache *c;
|
||||
if (!bytes) bytes = 10 * 1024 * 1024;
|
||||
if (lifetime <= 0) lifetime = 24 * 60 * 60;
|
||||
ents = rounddown2pow(MAX(2, bytes / sizeof(struct SslCacheEntry)));
|
||||
size = sizeof(struct SslCache) + sizeof(struct SslCacheEntry) * ents;
|
||||
size = ROUNDUP(size, FRAMESIZE);
|
||||
c = OpenSslCache(path, size);
|
||||
if (!c) c = mmap(0, size, PROT, FLAGS | MAP_ANONYMOUS, -1, 0);
|
||||
CHECK_NE(MAP_FAILED, c);
|
||||
VERBOSEF("opened %`'s %,zu bytes with %,u slots",
|
||||
c ? path : "anonymous shared memory", size, ents);
|
||||
c->lifetime = lifetime;
|
||||
c->size = size;
|
||||
c->mask = ents - 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
void FreeSslCache(struct SslCache *cache) {
|
||||
if (!cache) return;
|
||||
CHECK_NE(-1, munmap(cache, cache->size));
|
||||
}
|
||||
|
||||
int UncacheSslSession(void *data, mbedtls_ssl_session *session) {
|
||||
int64_t ts;
|
||||
uint64_t tick;
|
||||
unsigned char *ticket;
|
||||
struct SslCache *cache;
|
||||
mbedtls_x509_crt *cert;
|
||||
struct SslCacheEntry *e;
|
||||
uint32_t i, hash, ticketlen;
|
||||
INFOF("uncache");
|
||||
cache = data;
|
||||
hash = HashSslSession(session);
|
||||
i = hash & cache->mask;
|
||||
e = cache->p + i;
|
||||
if (!(tick = e->tick) || hash != e->hash) {
|
||||
NOISEF("%u empty", i);
|
||||
return 1;
|
||||
}
|
||||
asm volatile("" ::: "memory");
|
||||
if (session->ciphersuite != e->session.ciphersuite ||
|
||||
session->compression != e->session.compression ||
|
||||
session->id_len != e->session.id_len ||
|
||||
memcmp(session->id, e->session.id, e->session.id_len)) {
|
||||
VERBOSEF("%u sslcache collision", i);
|
||||
return 1;
|
||||
}
|
||||
ts = time(0);
|
||||
if (!(e->time <= ts && ts <= e->time + cache->lifetime)) {
|
||||
DEBUGF("%u sslcache expired", i);
|
||||
_cmpxchg(&e->tick, tick, 0);
|
||||
return 1;
|
||||
}
|
||||
cert = 0;
|
||||
ticket = 0;
|
||||
if ((e->certlen && (!(cert = calloc(1, sizeof(*cert))) ||
|
||||
mbedtls_x509_crt_parse_der(cert, e->cert, e->certlen)))) {
|
||||
goto Contention;
|
||||
}
|
||||
if ((ticketlen = e->ticketlen)) {
|
||||
if (!(ticket = malloc(ticketlen))) goto Contention;
|
||||
memcpy(ticket, e->ticket, ticketlen);
|
||||
}
|
||||
mbedtls_ssl_session_free(session);
|
||||
memcpy(session, &e->session, sizeof(*session));
|
||||
asm volatile("" ::: "memory");
|
||||
if (tick != e->tick) goto Contention;
|
||||
session->peer_cert = cert;
|
||||
session->ticket = ticket;
|
||||
session->ticket_len = ticketlen;
|
||||
DEBUGF("%u restored ssl from cache", i);
|
||||
return 0;
|
||||
Contention:
|
||||
WARNF("%u sslcache contention 0x%08x", i, hash);
|
||||
mbedtls_x509_crt_free(cert);
|
||||
free(ticket);
|
||||
free(cert);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CacheSslSession(void *data, const mbedtls_ssl_session *session) {
|
||||
int pid;
|
||||
uint64_t tick;
|
||||
uint32_t i, hash;
|
||||
struct SslCache *cache;
|
||||
struct SslCacheEntry *e;
|
||||
cache = data;
|
||||
if (session->peer_cert &&
|
||||
session->peer_cert->raw.len > sizeof(cache->p[0].cert)) {
|
||||
WARNF("%s too big %zu", "cert", session->peer_cert->raw.len);
|
||||
return 1;
|
||||
}
|
||||
if (session->ticket && session->ticket_len > sizeof(cache->p[0].ticket)) {
|
||||
WARNF("%s too big %zu", "ticket", session->ticket_len);
|
||||
return 1;
|
||||
}
|
||||
pid = getpid();
|
||||
hash = HashSslSession(session);
|
||||
i = hash & cache->mask;
|
||||
e = cache->p + i;
|
||||
e->tick = 0;
|
||||
e->pid = pid;
|
||||
asm volatile("" ::: "memory");
|
||||
memcpy(&e->session, session, sizeof(*session));
|
||||
if (session->peer_cert) {
|
||||
e->certlen = session->peer_cert->raw.len;
|
||||
memcpy(e->cert, session->peer_cert->raw.p, session->peer_cert->raw.len);
|
||||
} else {
|
||||
e->certlen = 0;
|
||||
}
|
||||
if (session->ticket) {
|
||||
e->ticketlen = session->ticket_len;
|
||||
memcpy(e->ticket, session->ticket, session->ticket_len);
|
||||
} else {
|
||||
e->ticketlen = 0;
|
||||
}
|
||||
e->hash = hash;
|
||||
e->time = time(0);
|
||||
tick = rdtsc();
|
||||
asm volatile("" ::: "memory");
|
||||
if (tick && _cmpxchg(&e->pid, pid, 0)) {
|
||||
DEBUGF("%u saved %s%s %`#.*s", i,
|
||||
mbedtls_ssl_get_ciphersuite_name(session->ciphersuite),
|
||||
session->compression ? " DEFLATE" : "", session->id_len,
|
||||
session->id);
|
||||
e->tick = tick;
|
||||
return 0;
|
||||
} else {
|
||||
WARNF("%u congestion", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef COSMOPOLITAN_NET_HTTPS_SSLCACHE_H_
|
||||
#define COSMOPOLITAN_NET_HTTPS_SSLCACHE_H_
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct SslCache {
|
||||
size_t size;
|
||||
int lifetime;
|
||||
uint32_t mask;
|
||||
struct SslCacheEntry {
|
||||
int64_t time;
|
||||
volatile uint64_t tick;
|
||||
volatile int pid;
|
||||
uint32_t hash;
|
||||
unsigned certlen;
|
||||
unsigned ticketlen;
|
||||
mbedtls_ssl_session session;
|
||||
uint8_t cert[1500];
|
||||
uint8_t ticket[500];
|
||||
} p[];
|
||||
};
|
||||
|
||||
struct SslCache *CreateSslCache(const char *, size_t, int);
|
||||
void FreeSslCache(struct SslCache *);
|
||||
int UncacheSslSession(void *, mbedtls_ssl_session *);
|
||||
int CacheSslSession(void *, const mbedtls_ssl_session *);
|
||||
char *GetSslCacheFile(void);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_NET_HTTPS_SSLCACHE_H_ */
|
|
@ -20,7 +20,7 @@
|
|||
#include "third_party/mbedtls/error.h"
|
||||
|
||||
char *GetTlsError(int r) {
|
||||
static char b[128];
|
||||
static _Thread_local char b[128];
|
||||
mbedtls_strerror(r, b, sizeof(b));
|
||||
return b;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -69,7 +70,10 @@ TEST(dup, bigNumber) {
|
|||
|
||||
#ifdef __x86_64__
|
||||
TEST(dup, clearsCloexecFlag) {
|
||||
static bool once;
|
||||
int ws;
|
||||
ASSERT_FALSE(once);
|
||||
once = true;
|
||||
ASSERT_SYS(0, 0, close(creat("file", 0644)));
|
||||
ASSERT_SYS(0, 3, open("file", O_RDWR | O_CLOEXEC));
|
||||
ASSERT_NE(-1, (ws = xspawn(0)));
|
||||
|
@ -79,7 +83,7 @@ TEST(dup, clearsCloexecFlag) {
|
|||
(char *const[]){GetProgramExecutableName(), "boop", 0});
|
||||
_exit(127);
|
||||
}
|
||||
ASSERT_EQ(72, WEXITSTATUS(ws));
|
||||
ASSERT_EQ(72 << 8, ws);
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -57,6 +57,17 @@ TEST(write, badMemory_efault) {
|
|||
ASSERT_SYS(EFAULT, -1, write(1, (void *)1, 1));
|
||||
}
|
||||
|
||||
TEST(write, brokenPipe_raisesSigpipe) {
|
||||
int fds[2];
|
||||
SPAWN(fork);
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
ASSERT_SYS(0, 0, pipe(fds));
|
||||
ASSERT_SYS(0, 1, write(4, "x", 1));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
write(4, "x", 1);
|
||||
TERMS(SIGPIPE);
|
||||
}
|
||||
|
||||
TEST(write, brokenPipe_sigpipeIgnored_returnsEpipe) {
|
||||
int fds[2];
|
||||
SPAWN(fork);
|
||||
|
|
|
@ -20,9 +20,13 @@
|
|||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/sysconf.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
|
@ -81,6 +85,21 @@ TEST(memmove, bighug) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(memmove, pageOverlapTorture) {
|
||||
long pagesz = sysconf(_SC_PAGESIZE);
|
||||
char *map = mmap(0, pagesz * 2, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
char *map2 = mmap(0, pagesz * 2, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
ASSERT_SYS(0, 0, mprotect(map + pagesz, pagesz, PROT_NONE));
|
||||
ASSERT_SYS(0, 0, mprotect(map2 + pagesz, pagesz, PROT_NONE));
|
||||
strcpy(map + pagesz - 9, "12345678");
|
||||
strcpy(map2 + pagesz - 9, "12345679");
|
||||
__expropriate(memmove(map + pagesz - 9, map2 + pagesz - 9, 9));
|
||||
EXPECT_SYS(0, 0, munmap(map2, pagesz * 2));
|
||||
EXPECT_SYS(0, 0, munmap(map, pagesz * 2));
|
||||
}
|
||||
|
||||
BENCH(memmove, bench) {
|
||||
int n, max = 128 * 1024 * 1024;
|
||||
char *volatile p = gc(calloc(max, 1));
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "net/http/escape.h"
|
||||
#ifdef __x86_64__
|
||||
|
||||
#if 0
|
||||
__static_yoink("backtrace.com");
|
||||
__static_yoink("backtrace.com.dbg");
|
||||
|
||||
|
@ -115,20 +116,12 @@ TEST(ShowCrashReports, testMemoryLeakCrash) {
|
|||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(78, WEXITSTATUS(ws));
|
||||
if (!strstr(output, "UNFREED MEMORY")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't report leak\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
// tinyprint(2, _gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(78 << 8, ws);
|
||||
ASSERT_TRUE(!!strstr(output, "UNFREED MEMORY"));
|
||||
if (IsAsan()) {
|
||||
if (!OutputHasSymbol(output, "strdup") ||
|
||||
!OutputHasSymbol(output, "MemoryLeakCrash")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't backtrace allocation\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "strdup") &&
|
||||
OutputHasSymbol(output, "MemoryLeakCrash"));
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
@ -215,15 +208,16 @@ TEST(ShowCrashReports, testDivideByZero) {
|
|||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
assert(128 + SIGFPE == WEXITSTATUS(ws) || 77 == WEXITSTATUS(ws));
|
||||
// tinyprint(2, _gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
if (IsModeDbg()) {
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
} else {
|
||||
EXPECT_TRUE(WIFSIGNALED(ws));
|
||||
EXPECT_EQ(SIGFPE, WTERMSIG(ws));
|
||||
}
|
||||
/* NULL is stopgap until we can copy symbol tables into binary */
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
if (!OutputHasSymbol(output, "FpuCrash")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "FpuCrash"));
|
||||
#endif
|
||||
if (strstr(output, "divrem overflow")) {
|
||||
// UBSAN handled it
|
||||
|
@ -339,21 +333,18 @@ TEST(ShowCrashReports, testBssOverrunCrash) {
|
|||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(77, WEXITSTATUS(ws));
|
||||
// tinyprint(2, _gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
/* NULL is stopgap until we can copy symbol tablces into binary */
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
if (!OutputHasSymbol(output, "BssOverrunCrash")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "BssOverrunCrash"));
|
||||
#endif
|
||||
if (!strstr(output, "'int' index 10 into 'char [10]' out of bounds") &&
|
||||
(!strstr(output, "☺☻♥♦♣♠•◘○") || !strstr(output, "global redzone"))) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have memory diagram\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
if (IsAsan()) {
|
||||
ASSERT_TRUE(
|
||||
!!strstr(output, "'int' index 10 into 'char [10]' out of bounds"));
|
||||
} else {
|
||||
ASSERT_TRUE(!!strstr(output, "☺☻♥♦♣♠•◘○"));
|
||||
ASSERT_TRUE(!!strstr(output, "global redzone"));
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
@ -417,30 +408,15 @@ TEST(ShowCrashReports, testNpeCrash) {
|
|||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(77, WEXITSTATUS(ws));
|
||||
// tinyprint(2, _gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
/* NULL is stopgap until we can copy symbol tables into binary */
|
||||
if (!strstr(output, "null pointer")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't diagnose the problem\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
ASSERT_TRUE(!!strstr(output, "null pointer"));
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
if (!OutputHasSymbol(output, "NpeCrash")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "NpeCrash"));
|
||||
#endif
|
||||
if (strstr(output, "null pointer access")) {
|
||||
// ubsan nailed it
|
||||
} else {
|
||||
// asan nailed it
|
||||
if (!strstr(output, "∅∅∅∅")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have shadow diagram\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
if (!strstr(output, "null pointer access")) { // ubsan
|
||||
ASSERT_TRUE(!!strstr(output, "∅∅∅∅")); // asan
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
@ -476,21 +452,15 @@ TEST(ShowCrashReports, testDataOverrunCrash) {
|
|||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(77, WEXITSTATUS(ws));
|
||||
// tinyprint(2, _gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
/* NULL is stopgap until we can copy symbol tablces into binary */
|
||||
#ifdef __FNO_OMIT_FRAME_POINTER__
|
||||
if (!OutputHasSymbol(output, "DataOverrunCrash")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have backtrace\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
}
|
||||
ASSERT_TRUE(OutputHasSymbol(output, "DataOverrunCrash"));
|
||||
#endif
|
||||
if (!strstr(output, "'int' index 10 into 'char [10]' out of bounds") &&
|
||||
(!strstr(output, "☺☻♥♦♣♠•◘○") || !strstr(output, "global redzone"))) {
|
||||
fprintf(stderr, "ERROR: crash report didn't have memory diagram\n%s\n",
|
||||
_gc(IndentLines(output, -1, 0, 4)));
|
||||
__die();
|
||||
if (!strstr(output, "'int' index 10 into 'char [10]' out")) { // ubsan
|
||||
ASSERT_TRUE(!!strstr(output, "☺☻♥♦♣♠•◘○")); // asan
|
||||
ASSERT_TRUE(!!strstr(output, "global redzone")); // asan
|
||||
}
|
||||
free(output);
|
||||
}
|
||||
|
@ -530,9 +500,13 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) {
|
|||
}
|
||||
close(fds[0]);
|
||||
ASSERT_NE(-1, wait(&ws));
|
||||
EXPECT_TRUE(WIFEXITED(ws));
|
||||
EXPECT_EQ(0, WTERMSIG(ws));
|
||||
EXPECT_EQ(IsAsan() ? 77 : 128 + SIGSEGV, WEXITSTATUS(ws));
|
||||
// tinyprint(2, _gc(IndentLines(output, -1, 0, 4)), "\n", NULL);
|
||||
if (IsModeDbg()) {
|
||||
EXPECT_EQ(77 << 8, ws);
|
||||
} else {
|
||||
EXPECT_TRUE(WIFSIGNALED(ws));
|
||||
EXPECT_EQ(SIGSEGV, WTERMSIG(ws));
|
||||
}
|
||||
/* NULL is stopgap until we can copy symbol tables into binary */
|
||||
if (!strstr(output, IsAsan() ? "null pointer" : "Uncaught SIGSEGV (SEGV_")) {
|
||||
fprintf(stderr, "ERROR: crash report didn't diagnose the problem\n%s\n",
|
||||
|
@ -548,5 +522,6 @@ TEST(ShowCrashReports, testNpeCrashAfterFinalize) {
|
|||
#endif
|
||||
free(output);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __x86_64__ */
|
||||
|
|
|
@ -48,11 +48,6 @@
|
|||
#define N 1024
|
||||
#define M 20
|
||||
|
||||
void SetUp(void) {
|
||||
// TODO(jart): what is wrong?
|
||||
if (IsWindows()) exit(0);
|
||||
}
|
||||
|
||||
TEST(malloc, zero) {
|
||||
char *p;
|
||||
ASSERT_NE(NULL, (p = malloc(0)));
|
||||
|
|
|
@ -36,7 +36,6 @@ void OnSignal(int sig, siginfo_t *si, void *ctx) {
|
|||
}
|
||||
|
||||
TEST(sigsetjmp, test) {
|
||||
if (IsWindows()) return; // no sigusr1 support
|
||||
sigset_t ss;
|
||||
int i, n = 1000;
|
||||
struct sigaction sa = {.sa_sigaction = OnSignal};
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
char testlib_enable_tmp_setup_teardown;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
__enable_threads();
|
||||
if (IsNetbsd()) exit(0);
|
||||
if (IsOpenbsd()) exit(0);
|
||||
ASSERT_SYS(0, 0, pledge("stdio rpath wpath cpath proc inet", 0));
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/sock.h"
|
||||
|
@ -151,6 +152,7 @@ __attribute__((__constructor__)) static void StdioPro(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
TEST(socket, canBeUsedAsExecutedStdio) {
|
||||
if (IsWindows()) return; // TODO(jart): What broke this?
|
||||
char buf[16] = {0};
|
||||
const char *prog;
|
||||
uint32_t addrsize = sizeof(struct sockaddr_in);
|
||||
|
|
|
@ -53,7 +53,6 @@ TEST(fwrite, test) {
|
|||
ASSERT_NE(NULL, (f = fopen(PATH, "a+b")));
|
||||
EXPECT_EQ(5, fwrite("hello", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
EXPECT_TRUE(!memcmp(buf, "hellohello", 10));
|
||||
|
@ -77,7 +76,6 @@ TEST(fwrite, testSmallBuffer) {
|
|||
setbuffer(f, gc(malloc(1)), 1);
|
||||
EXPECT_EQ(5, fwrite("hello", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setbuffer(f, gc(malloc(1)), 1);
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
|
@ -106,7 +104,6 @@ TEST(fwrite, testLineBuffer) {
|
|||
setvbuf(f, NULL, _IOLBF, 64);
|
||||
EXPECT_EQ(5, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setvbuf(f, NULL, _IOLBF, 64);
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
|
@ -131,7 +128,6 @@ TEST(fwrite, testNoBuffer) {
|
|||
setvbuf(f, NULL, _IONBF, 64);
|
||||
EXPECT_EQ(5, fwrite("heyy\n", 1, 5, f));
|
||||
EXPECT_NE(-1, fclose(f));
|
||||
if (IsWindows()) return;
|
||||
ASSERT_NE(NULL, (f = fopen(PATH, "r")));
|
||||
setvbuf(f, NULL, _IONBF, 64);
|
||||
EXPECT_EQ(10, fread(buf, 1, 10, f));
|
||||
|
|
|
@ -145,8 +145,8 @@ void *Worker(void *arg) {
|
|||
strcat(arg1, "\n");
|
||||
strcat(arg2, "\n");
|
||||
ASSERT_NE(NULL, (f = popen(cmd, "r")));
|
||||
ASSERT_STREQ(arg1, fgets(buf, sizeof(buf), f));
|
||||
ASSERT_STREQ(arg2, fgets(buf, sizeof(buf), f));
|
||||
EXPECT_STREQ(arg1, fgets(buf, sizeof(buf), f));
|
||||
EXPECT_STREQ(arg2, fgets(buf, sizeof(buf), f));
|
||||
ASSERT_EQ(0, pclose(f));
|
||||
free(arg2);
|
||||
free(arg1);
|
||||
|
@ -156,6 +156,10 @@ void *Worker(void *arg) {
|
|||
}
|
||||
|
||||
TEST(popen, torture) {
|
||||
if (IsWindows()) {
|
||||
// TODO: Why does pclose() return kNtSignalAccessViolationa?!
|
||||
return;
|
||||
}
|
||||
int i, n = 4;
|
||||
pthread_t *t = _gc(malloc(sizeof(pthread_t) * n));
|
||||
testlib_extract("/zip/echo.com", "echo.com", 0755);
|
||||
|
|
|
@ -122,8 +122,9 @@ TEST(posix_spawn, torture) {
|
|||
ASSERT_EQ(0, posix_spawn(&pid, "./echo.com", &fa, &attr, args, envs));
|
||||
ASSERT_FALSE(__vforked);
|
||||
ASSERT_NE(-1, waitpid(pid, &ws, 0));
|
||||
ASSERT_TRUE(WIFEXITED(ws));
|
||||
ASSERT_EQ(0, WEXITSTATUS(ws));
|
||||
EXPECT_FALSE(WIFSIGNALED(ws));
|
||||
EXPECT_EQ(0, WTERMSIG(ws));
|
||||
EXPECT_EQ(0, WEXITSTATUS(ws));
|
||||
close(fd);
|
||||
free(zzz);
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ void *Torturer(void *arg) {
|
|||
}
|
||||
|
||||
TEST(posix_spawn, agony) {
|
||||
int i, n = 3;
|
||||
int i, n = 4;
|
||||
pthread_t *t = _gc(malloc(sizeof(pthread_t) * n));
|
||||
testlib_extract("/zip/echo.com", "echo.com", 0755);
|
||||
for (i = 0; i < n; ++i) {
|
||||
|
@ -158,7 +159,7 @@ TEST(posix_spawn, agony) {
|
|||
|
||||
void BenchmarkProcessLifecycle(void) {
|
||||
int ws, pid;
|
||||
char *prog = "/tmp/tiny64";
|
||||
char *prog = "tiny64";
|
||||
char *args[] = {"tiny64", NULL};
|
||||
char *envs[] = {NULL};
|
||||
ASSERT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
|
||||
|
@ -189,11 +190,11 @@ const char kTinyLinuxExit[128] = {
|
|||
/* BENCH(spawn, bench) { */
|
||||
/* int fd; */
|
||||
/* if (IsLinux()) { */
|
||||
/* fd = open("/tmp/tiny64", O_CREAT | O_TRUNC | O_WRONLY, 0755); */
|
||||
/* fd = open("tiny64", O_CREAT | O_TRUNC | O_WRONLY, 0755); */
|
||||
/* write(fd, kTinyLinuxExit, 128); */
|
||||
/* close(fd); */
|
||||
/* EZBENCH2("spawn", donothing, BenchmarkProcessLifecycle()); */
|
||||
/* unlink("/tmp/tiny64"); */
|
||||
/* unlink("tiny64"); */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/nexgen32e/crc32.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
|
|
@ -16,14 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sched_param.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigaltstack.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/nexgen32e.h"
|
||||
#include "libc/nexgen32e/vendor.internal.h"
|
||||
|
@ -33,6 +37,7 @@
|
|||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sched.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/ss.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
@ -246,6 +251,58 @@ TEST(pthread_cleanup, pthread_normal) {
|
|||
ASSERT_TRUE(g_cleanup2);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HOW TO PROTECT YOUR THREADS FROM STACK OVERFLOW
|
||||
// note that sigaltstack is waq on the main thread
|
||||
|
||||
jmp_buf recover;
|
||||
volatile bool smashed_stack;
|
||||
|
||||
void CrashHandler(int sig) {
|
||||
smashed_stack = true;
|
||||
longjmp(recover, 123);
|
||||
}
|
||||
|
||||
int StackOverflow(int f(), int n) {
|
||||
if (n < INT_MAX) {
|
||||
return f(f, n + 1) - 1;
|
||||
} else {
|
||||
return INT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
int (*pStackOverflow)(int (*)(), int) = StackOverflow;
|
||||
|
||||
void *MyPosixThread(void *arg) {
|
||||
int jumpcode;
|
||||
struct sigaction sa, o1, o2;
|
||||
struct sigaltstack ss = {
|
||||
.ss_sp = gc(malloc(SIGSTKSZ)),
|
||||
.ss_size = SIGSTKSZ,
|
||||
};
|
||||
sigaltstack(&ss, 0);
|
||||
sa.sa_flags = SA_ONSTACK; // <-- important
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = CrashHandler;
|
||||
sigaction(SIGBUS, &sa, &o1);
|
||||
sigaction(SIGSEGV, &sa, &o2);
|
||||
if (!(jumpcode = setjmp(recover))) {
|
||||
exit(pStackOverflow(pStackOverflow, 0));
|
||||
}
|
||||
ASSERT_EQ(123, jumpcode);
|
||||
sigaction(SIGSEGV, &o2, 0);
|
||||
sigaction(SIGBUS, &o1, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(cosmo, altstack_thread) {
|
||||
pthread_t th;
|
||||
if (IsWindows()) return;
|
||||
pthread_create(&th, 0, MyPosixThread, 0);
|
||||
pthread_join(th, 0);
|
||||
ASSERT_TRUE(smashed_stack);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BENCHMARKS
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -31,29 +33,40 @@
|
|||
#include "libc/thread/semaphore.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
pthread_barrier_t barrier;
|
||||
char testlib_enable_tmp_setup_teardown;
|
||||
#if 0 // TODO(jart): delete these stupid multi-process semaphores
|
||||
|
||||
void SetUp(void) {
|
||||
// TODO(jart): Fix shocking GitHub Actions error.
|
||||
if (getenv("CI")) exit(0);
|
||||
sem_unlink("/fooz");
|
||||
sem_unlink("/barz");
|
||||
}
|
||||
char name1[PATH_MAX];
|
||||
char name2[PATH_MAX];
|
||||
pthread_barrier_t barrier;
|
||||
|
||||
void IgnoreStderr(void) {
|
||||
close(2);
|
||||
open("/dev/null", O_WRONLY);
|
||||
}
|
||||
|
||||
const char *SemPath(const char *name) {
|
||||
static _Thread_local char buf[PATH_MAX];
|
||||
return sem_path_np(name, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void SetUp(void) {
|
||||
mktemp(strcpy(name1, "/tmp/sem_open_test.name1.XXXXXX"));
|
||||
mktemp(strcpy(name2, "/tmp/sem_open_test.name2.XXXXXX"));
|
||||
}
|
||||
|
||||
void TearDown(void) {
|
||||
ASSERT_FALSE(fileexists(SemPath(name2)));
|
||||
ASSERT_FALSE(fileexists(SemPath(name1)));
|
||||
}
|
||||
|
||||
void *Worker(void *arg) {
|
||||
sem_t *a, *b;
|
||||
struct timespec ts;
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open("/fooz", 0)));
|
||||
ASSERT_EQ((sem_t *)arg, a);
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open("/barz", 0)));
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open(name1, 0)));
|
||||
EXPECT_EQ((sem_t *)arg, a);
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open(name2, 0)));
|
||||
if (pthread_barrier_wait(&barrier) == PTHREAD_BARRIER_SERIAL_THREAD) {
|
||||
ASSERT_SYS(0, 0, sem_unlink("/fooz"));
|
||||
ASSERT_SYS(0, 0, sem_unlink(name1));
|
||||
}
|
||||
ASSERT_SYS(0, 0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||
ts.tv_sec += 1;
|
||||
|
@ -72,14 +85,12 @@ void *Worker(void *arg) {
|
|||
// 4. semaphore may be unlinked before it's closed, from threads
|
||||
TEST(sem_open, test) {
|
||||
sem_t *a, *b;
|
||||
int i, r, n = 4;
|
||||
int i, r, n = 8;
|
||||
pthread_t *t = _gc(malloc(sizeof(pthread_t) * n));
|
||||
sem_unlink("/fooz");
|
||||
sem_unlink("/barz");
|
||||
errno = 0;
|
||||
ASSERT_EQ(0, pthread_barrier_init(&barrier, 0, n));
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open("/fooz", O_CREAT, 0644, 0)));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open("/barz", O_CREAT, 0644, 0)));
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open(name2, O_CREAT, 0644, 0)));
|
||||
ASSERT_SYS(0, 0, sem_getvalue(a, &r));
|
||||
ASSERT_EQ(0, r);
|
||||
ASSERT_SYS(0, 0, sem_getvalue(b, &r));
|
||||
|
@ -90,8 +101,8 @@ TEST(sem_open, test) {
|
|||
ASSERT_EQ(0, r);
|
||||
for (i = 0; i < n; ++i) ASSERT_SYS(0, 0, sem_post(b));
|
||||
for (i = 0; i < n; ++i) ASSERT_EQ(0, pthread_join(t[i], 0));
|
||||
ASSERT_SYS(0, 0, sem_unlink("/barz"));
|
||||
ASSERT_SYS(0, 0, sem_getvalue(b, &r));
|
||||
EXPECT_SYS(0, 0, sem_unlink(name2));
|
||||
EXPECT_SYS(0, 0, sem_getvalue(b, &r));
|
||||
ASSERT_EQ(0, r);
|
||||
ASSERT_SYS(0, 0, sem_close(b));
|
||||
ASSERT_FALSE(testlib_memoryexists(b));
|
||||
|
@ -111,28 +122,24 @@ TEST(sem_close, withUnnamedSemaphore_isUndefinedBehavior) {
|
|||
ASSERT_SYS(0, 0, sem_destroy(&sem));
|
||||
}
|
||||
|
||||
TEST(sem_destroy, withNamedSemaphore_isUndefinedBehavior) {
|
||||
if (!IsModeDbg()) return;
|
||||
sem_t *sem;
|
||||
ASSERT_NE(SEM_FAILED, (sem = sem_open("/boop", O_CREAT, 0644, 0)));
|
||||
TEST(sem_open, inheritAcrossFork1) {
|
||||
sem_t *a;
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
SPAWN(fork);
|
||||
IgnoreStderr();
|
||||
sem_destroy(sem);
|
||||
TERMS(SIGABRT); // see __assert_fail
|
||||
ASSERT_SYS(0, 0, sem_unlink("/boop"));
|
||||
ASSERT_SYS(0, 0, sem_close(sem));
|
||||
EXITS(0);
|
||||
ASSERT_SYS(0, 0, sem_close(a));
|
||||
}
|
||||
|
||||
TEST(sem_open, inheritAcrossFork) {
|
||||
TEST(sem_open, inheritAcrossFork2) {
|
||||
sem_t *a, *b;
|
||||
struct timespec ts;
|
||||
ASSERT_SYS(0, 0, clock_gettime(CLOCK_REALTIME, &ts));
|
||||
ts.tv_sec += 1;
|
||||
errno = 0;
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open("/fooz", O_CREAT, 0644, 0)));
|
||||
ASSERT_SYS(0, 0, sem_unlink("/fooz"));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open("/barz", O_CREAT, 0644, 0)));
|
||||
ASSERT_SYS(0, 0, sem_unlink("/barz"));
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
ASSERT_SYS(0, 0, sem_unlink(name1));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open(name2, O_CREAT, 0644, 0)));
|
||||
ASSERT_SYS(0, 0, sem_unlink(name2));
|
||||
SPAWN(fork);
|
||||
ASSERT_SYS(0, 0, sem_post(a));
|
||||
ASSERT_SYS(0, 0, sem_wait(b));
|
||||
|
@ -146,30 +153,36 @@ TEST(sem_open, inheritAcrossFork) {
|
|||
ASSERT_FALSE(testlib_memoryexists(a));
|
||||
}
|
||||
|
||||
TEST(sem_open, openReadonlyAfterUnlink_enoent) {
|
||||
TEST(sem_open, openExistsAfterUnlink_enoent) {
|
||||
sem_t *sem;
|
||||
sem_unlink("/fooz");
|
||||
ASSERT_NE(SEM_FAILED, (sem = sem_open("/fooz", O_CREAT, 0644, 0)));
|
||||
ASSERT_EQ(0, sem_unlink("/fooz"));
|
||||
ASSERT_EQ(SEM_FAILED, sem_open("/fooz", O_RDONLY));
|
||||
ASSERT_NE(SEM_FAILED, (sem = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
ASSERT_EQ(0, sem_unlink(name1));
|
||||
ASSERT_EQ(SEM_FAILED, sem_open(name1, 0));
|
||||
ASSERT_EQ(ENOENT, errno);
|
||||
ASSERT_EQ(0, sem_close(sem));
|
||||
}
|
||||
|
||||
TEST(sem_open, openReadonlyAfterIndependentUnlinkAndRecreate_returnsNewOne) {
|
||||
if (1) return;
|
||||
TEST(sem_open, openExistsRecursive) {
|
||||
sem_t *sem1, *sem2;
|
||||
ASSERT_NE(SEM_FAILED, (sem1 = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
ASSERT_NE(SEM_FAILED, (sem2 = sem_open(name1, 0)));
|
||||
ASSERT_EQ(0, sem_close(sem2));
|
||||
ASSERT_EQ(0, sem_close(sem1));
|
||||
}
|
||||
|
||||
TEST(sem_open, openExistsAfterIndependentUnlinkAndRecreate_returnsNewOne) {
|
||||
sem_t *a, *b;
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open("/fooz", O_CREAT, 0644, 0)));
|
||||
ASSERT_NE(SEM_FAILED, (a = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
SPAWN(fork);
|
||||
ASSERT_EQ(0, sem_unlink("/fooz"));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open("/fooz", O_CREAT, 0644, 0)));
|
||||
ASSERT_EQ(0, sem_unlink(name1));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open(name1, O_CREAT, 0644, 0)));
|
||||
ASSERT_NE(a, b);
|
||||
ASSERT_SYS(0, 0, sem_post(a));
|
||||
ASSERT_SYS(0, 0, sem_wait(b));
|
||||
ASSERT_EQ(0, sem_close(b));
|
||||
PARENT();
|
||||
ASSERT_SYS(0, 0, sem_wait(a));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open("/fooz", O_RDONLY)));
|
||||
ASSERT_NE(SEM_FAILED, (b = sem_open(name1, 0)));
|
||||
ASSERT_NE(a, b);
|
||||
ASSERT_SYS(0, 0, sem_post(b));
|
||||
ASSERT_EQ(0, sem_close(b));
|
||||
|
@ -189,3 +202,5 @@ TEST(sem_close, openTwiceCloseOnce_stillMapped) {
|
|||
ASSERT_SYS(0, 0, sem_post(a));
|
||||
ASSERT_SYS(0, 0, sem_close(b));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
48
third_party/mbedtls/test/lib.c
vendored
48
third_party/mbedtls/test/lib.c
vendored
|
@ -15,13 +15,16 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
#include "third_party/mbedtls/test/lib.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/describebacktrace.internal.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
|
@ -38,7 +41,9 @@
|
|||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
#include "libc/temp.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "third_party/mbedtls/config.h"
|
||||
#include "third_party/mbedtls/endian.h"
|
||||
|
@ -76,11 +81,33 @@ char *output;
|
|||
jmp_buf jmp_tmp;
|
||||
int option_verbose = 1;
|
||||
mbedtls_test_info_t mbedtls_test_info;
|
||||
static char tmpdir[PATH_MAX];
|
||||
static char third_party[PATH_MAX];
|
||||
|
||||
int mbedtls_test_platform_setup(void) {
|
||||
int ret = 0;
|
||||
const char *s;
|
||||
static char mybuf[2][BUFSIZ];
|
||||
ShowCrashReports();
|
||||
if ((s = getenv("TMPDIR"))) {
|
||||
strlcpy(tmpdir, s, sizeof(tmpdir));
|
||||
if (makedirs(tmpdir, 0755)) {
|
||||
strcpy(tmpdir, "/tmp");
|
||||
}
|
||||
} else {
|
||||
strcpy(tmpdir, "/tmp");
|
||||
}
|
||||
s = realpath("third_party/", third_party);
|
||||
strlcat(tmpdir, "/mbedtls.XXXXXX", sizeof(tmpdir));
|
||||
if (!mkdtemp(tmpdir)) {
|
||||
perror(tmpdir);
|
||||
exit(1);
|
||||
}
|
||||
if (chdir(tmpdir)) {
|
||||
perror(tmpdir);
|
||||
exit(2);
|
||||
}
|
||||
if (s) symlink(s, "third_party");
|
||||
makedirs("o/tmp", 0755);
|
||||
setvbuf(stdout, mybuf[0], _IOLBF, BUFSIZ);
|
||||
setvbuf(stderr, mybuf[1], _IOLBF, BUFSIZ);
|
||||
|
@ -91,14 +118,20 @@ int mbedtls_test_platform_setup(void) {
|
|||
}
|
||||
|
||||
void mbedtls_test_platform_teardown(void) {
|
||||
rmrf(tmpdir);
|
||||
#if defined(MBEDTLS_PLATFORM_C)
|
||||
mbedtls_platform_teardown(&platform_ctx);
|
||||
#endif /* MBEDTLS_PLATFORM_C */
|
||||
}
|
||||
|
||||
wontreturn void exit(int rc) {
|
||||
if (rc) fprintf(stderr, "mbedtls test exit() called with %d\n", rc);
|
||||
if (rc) xwrite(1, output, appendz(output).i);
|
||||
if (rc) {
|
||||
fprintf(stderr, "mbedtls test exit() called with $?=%d bt %s\n", rc,
|
||||
DescribeBacktrace(__builtin_frame_address(0)));
|
||||
}
|
||||
if (rc) {
|
||||
xwrite(1, output, appendz(output).i);
|
||||
}
|
||||
free(output);
|
||||
output = 0;
|
||||
__cxa_finalize(0);
|
||||
|
@ -137,7 +170,16 @@ int mbedtls_test_write(const char *fmt, ...) {
|
|||
if (option_verbose) {
|
||||
n = vfprintf(stderr, fmt, va);
|
||||
} else {
|
||||
n = vappendf(&output, fmt, va);
|
||||
char buf[512];
|
||||
const char *s;
|
||||
vsnprintf(buf, 512, fmt, va);
|
||||
if ((s = strchr(buf, '\n')) && //
|
||||
s == buf + strlen(buf) - 1 && //
|
||||
strstr(buf, "PASS")) {
|
||||
n = 0; // ignore pointless success lines
|
||||
} else {
|
||||
n = appends(&output, buf);
|
||||
}
|
||||
}
|
||||
va_end(va);
|
||||
return n;
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
│ Python 3 │
|
||||
│ https://docs.python.org/3/license.html │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/thread/semaphore.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "third_party/python/Modules/_multiprocessing/multiprocessing.h"
|
||||
/* clang-format off */
|
||||
|
||||
|
@ -206,10 +206,6 @@ semlock_release(SemLockObject *self, PyObject *args)
|
|||
# define SEM_FAILED ((sem_t *)-1)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SEM_UNLINK
|
||||
# define sem_unlink(name) 0
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SEM_TIMEDWAIT
|
||||
# define sem_timedwait(sem,deadline) sem_timedwait_save(sem,deadline,_save)
|
||||
|
||||
|
|
2
third_party/vqsort/vqsort.mk
vendored
2
third_party/vqsort/vqsort.mk
vendored
|
@ -20,11 +20,11 @@ THIRD_PARTY_VQSORT_A_CHECKS = \
|
|||
$(THIRD_PARTY_VQSORT_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
THIRD_PARTY_VQSORT_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_INTRIN \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
THIRD_PARTY_COMPILER_RT
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ TOOL_BUILD_DIRECTDEPS = \
|
|||
LIBC_TIME \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_X \
|
||||
NET_HTTP \
|
||||
NET_HTTPS \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
THIRD_PARTY_GDTOA \
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/fmt/magnumstrs.internal.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/appendresourcereport.internal.h"
|
||||
|
@ -50,7 +49,7 @@
|
|||
#include "libc/nexgen32e/x86info.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/append.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/stdio/posix_spawn.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
|
@ -115,7 +114,7 @@ FLAGS\n\
|
|||
-C SECS set cpu limit [default 16]\n\
|
||||
-L SECS set lat limit [default 90]\n\
|
||||
-P PROCS set pro limit [default 2048]\n\
|
||||
-S BYTES set stk limit [default 2m]\n\
|
||||
-S BYTES set stk limit [default 8m]\n\
|
||||
-M BYTES set mem limit [default 512m]\n\
|
||||
-F BYTES set fsz limit [default 256m]\n\
|
||||
-O BYTES set out limit [default 1m]\n\
|
||||
|
@ -138,7 +137,8 @@ ENVIRONMENT\n\
|
|||
\n"
|
||||
|
||||
struct Strings {
|
||||
size_t n;
|
||||
int n;
|
||||
int c;
|
||||
char **p;
|
||||
};
|
||||
|
||||
|
@ -206,6 +206,8 @@ sigset_t mask;
|
|||
char buf[4096];
|
||||
sigset_t savemask;
|
||||
char tmpout[PATH_MAX];
|
||||
posix_spawnattr_t spawnattr;
|
||||
posix_spawn_file_actions_t spawnfila;
|
||||
|
||||
char *g_tmpout;
|
||||
const char *g_tmpout_original;
|
||||
|
@ -414,16 +416,20 @@ bool IsGccOnlyFlag(const char *s) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
if (startswith(s, "-ffixed-")) return true;
|
||||
if (startswith(s, "-fcall-saved")) return true;
|
||||
if (startswith(s, "-fcall-used")) return true;
|
||||
if (startswith(s, "-fgcse-")) return true;
|
||||
if (startswith(s, "-fvect-cost-model=")) return true;
|
||||
if (startswith(s, "-fsimd-cost-model=")) return true;
|
||||
if (startswith(s, "-fopt-info")) return true;
|
||||
if (startswith(s, "-mstringop-strategy=")) return true;
|
||||
if (startswith(s, "-mpreferred-stack-boundary=")) return true;
|
||||
if (startswith(s, "-Wframe-larger-than=")) return true;
|
||||
if (s[0] == '-') {
|
||||
if (s[1] == 'f') {
|
||||
if (startswith(s, "-ffixed-")) return true;
|
||||
if (startswith(s, "-fcall-saved")) return true;
|
||||
if (startswith(s, "-fcall-used")) return true;
|
||||
if (startswith(s, "-fgcse-")) return true;
|
||||
if (startswith(s, "-fvect-cost-model=")) return true;
|
||||
if (startswith(s, "-fsimd-cost-model=")) return true;
|
||||
if (startswith(s, "-fopt-info")) return true;
|
||||
}
|
||||
if (startswith(s, "-mstringop-strategy=")) return true;
|
||||
if (startswith(s, "-mpreferred-stack-boundary=")) return true;
|
||||
if (startswith(s, "-Wframe-larger-than=")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -446,9 +452,16 @@ static size_t TallyArgs(char **p) {
|
|||
}
|
||||
|
||||
void AddStr(struct Strings *l, char *s) {
|
||||
l->p = realloc(l->p, (++l->n + 1) * sizeof(*l->p));
|
||||
l->p[l->n - 1] = s;
|
||||
l->p[l->n - 0] = 0;
|
||||
if (l->n == l->c) {
|
||||
if (l->c) {
|
||||
l->c += l->c >> 1;
|
||||
} else {
|
||||
l->c = 16;
|
||||
}
|
||||
l->p = realloc(l->p, (l->c + 1) * sizeof(*l->p));
|
||||
}
|
||||
l->p[l->n++] = s;
|
||||
l->p[l->n] = 0;
|
||||
}
|
||||
|
||||
void AddEnv(char *s) {
|
||||
|
@ -510,58 +523,47 @@ static int GetBaseCpuFreqMhz(void) {
|
|||
return KCPUIDS(16H, EAX) & 0x7fff;
|
||||
}
|
||||
|
||||
void PlanResource(int resource, struct rlimit rlim) {
|
||||
struct rlimit prior;
|
||||
if (getrlimit(resource, &prior)) return;
|
||||
rlim.rlim_cur = MIN(rlim.rlim_cur, prior.rlim_max);
|
||||
rlim.rlim_max = MIN(rlim.rlim_max, prior.rlim_max);
|
||||
posix_spawnattr_setrlimit(&spawnattr, resource, &rlim);
|
||||
}
|
||||
|
||||
void SetCpuLimit(int secs) {
|
||||
if (secs <= 0) return;
|
||||
if (IsWindows()) return;
|
||||
#ifdef __x86_64__
|
||||
int mhz, lim;
|
||||
struct rlimit rlim;
|
||||
if (!(mhz = GetBaseCpuFreqMhz())) return;
|
||||
lim = ceil(3100. / mhz * secs);
|
||||
rlim.rlim_cur = lim;
|
||||
rlim.rlim_max = lim + 1;
|
||||
if (setrlimit(RLIMIT_CPU, &rlim) == -1) {
|
||||
if (getrlimit(RLIMIT_CPU, &rlim) == -1) return;
|
||||
if (lim < rlim.rlim_cur) {
|
||||
rlim.rlim_cur = lim;
|
||||
setrlimit(RLIMIT_CPU, &rlim);
|
||||
}
|
||||
}
|
||||
PlanResource(RLIMIT_CPU, (struct rlimit){lim, lim + 1});
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetFszLimit(long n) {
|
||||
struct rlimit rlim;
|
||||
if (n <= 0) return;
|
||||
if (IsWindows()) return;
|
||||
rlim.rlim_cur = n;
|
||||
rlim.rlim_max = n + (n >> 1);
|
||||
if (setrlimit(RLIMIT_FSIZE, &rlim) == -1) {
|
||||
if (getrlimit(RLIMIT_FSIZE, &rlim) == -1) return;
|
||||
rlim.rlim_cur = n;
|
||||
setrlimit(RLIMIT_FSIZE, &rlim);
|
||||
}
|
||||
PlanResource(RLIMIT_FSIZE, (struct rlimit){n, n + (n >> 1)});
|
||||
}
|
||||
|
||||
void SetMemLimit(long n) {
|
||||
struct rlimit rlim = {n, n};
|
||||
if (n <= 0) return;
|
||||
if (IsWindows() || IsXnu()) return;
|
||||
setrlimit(RLIMIT_AS, &rlim);
|
||||
PlanResource(RLIMIT_AS, (struct rlimit){n, n});
|
||||
}
|
||||
|
||||
void SetStkLimit(long n) {
|
||||
if (IsWindows()) return;
|
||||
if (n <= 0) return;
|
||||
n = MAX(n, PTHREAD_STACK_MIN * 2);
|
||||
struct rlimit rlim = {n, n};
|
||||
setrlimit(RLIMIT_STACK, &rlim);
|
||||
PlanResource(RLIMIT_STACK, (struct rlimit){n, n});
|
||||
}
|
||||
|
||||
void SetProLimit(long n) {
|
||||
struct rlimit rlim = {n, n};
|
||||
if (n <= 0) return;
|
||||
setrlimit(RLIMIT_NPROC, &rlim);
|
||||
PlanResource(RLIMIT_NPROC, (struct rlimit){n, n});
|
||||
}
|
||||
|
||||
bool ArgNeedsShellQuotes(const char *s) {
|
||||
|
@ -616,7 +618,7 @@ char *AddShellQuotes(const char *s) {
|
|||
|
||||
void MakeDirs(const char *path, int mode) {
|
||||
if (makedirs(path, mode)) {
|
||||
kprintf("error: makedirs(%#s) failed\n", path);
|
||||
perror(path);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -624,15 +626,29 @@ void MakeDirs(const char *path, int mode) {
|
|||
int Launch(void) {
|
||||
size_t got;
|
||||
ssize_t rc;
|
||||
errno_t err;
|
||||
int ws, pid;
|
||||
uint64_t us;
|
||||
gotchld = 0;
|
||||
|
||||
if (pipe2(pipefds, O_CLOEXEC) == -1) {
|
||||
kprintf("pipe2 failed: %s\n", _strerrno(errno));
|
||||
perror("pipe2");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
posix_spawnattr_init(&spawnattr);
|
||||
posix_spawnattr_setsigmask(&spawnattr, &savemask);
|
||||
SetCpuLimit(cpuquota);
|
||||
SetFszLimit(fszquota);
|
||||
SetMemLimit(memquota);
|
||||
SetStkLimit(stkquota);
|
||||
SetProLimit(proquota);
|
||||
|
||||
posix_spawn_file_actions_init(&spawnfila);
|
||||
if (stdoutmustclose)
|
||||
posix_spawn_file_actions_adddup2(&spawnfila, pipefds[1], 1);
|
||||
posix_spawn_file_actions_adddup2(&spawnfila, pipefds[1], 2);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
if (timeout > 0) {
|
||||
timer.it_value.tv_sec = timeout;
|
||||
|
@ -640,36 +656,17 @@ int Launch(void) {
|
|||
setitimer(ITIMER_REAL, &timer, 0);
|
||||
}
|
||||
|
||||
pid = vfork();
|
||||
if (pid == -1) {
|
||||
kprintf("vfork failed: %s\n", _strerrno(errno));
|
||||
err = posix_spawn(&pid, cmd, &spawnfila, &spawnattr, args.p, env.p);
|
||||
if (err) {
|
||||
tinyprint(2, program_invocation_short_name, ": failed to spawn ", cmd, ": ",
|
||||
strerror(err), " (see --strace for further details)\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int fd;
|
||||
size_t n;
|
||||
char b[1024], *p;
|
||||
size_t t = strlen(cmd) + 1 + TallyArgs(args.p) + 9 + TallyArgs(env.p) + 9;
|
||||
n = ksnprintf(b, sizeof(b), "%ld %s %s\n", t, cmd, outpath);
|
||||
fd = open("o/argmax.txt", O_APPEND | O_CREAT | O_WRONLY, 0644);
|
||||
write(fd, b, n);
|
||||
close(fd);
|
||||
#endif
|
||||
|
||||
if (!pid) {
|
||||
SetCpuLimit(cpuquota);
|
||||
SetFszLimit(fszquota);
|
||||
SetMemLimit(memquota);
|
||||
SetStkLimit(stkquota);
|
||||
SetProLimit(proquota);
|
||||
if (stdoutmustclose) dup2(pipefds[1], 1);
|
||||
dup2(pipefds[1], 2);
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
execve(cmd, args.p, env.p);
|
||||
kprintf("execve(%#s) failed: %s\n", cmd, _strerrno(errno));
|
||||
_Exit(127);
|
||||
}
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
posix_spawn_file_actions_destroy(&spawnfila);
|
||||
posix_spawnattr_destroy(&spawnattr);
|
||||
close(pipefds[1]);
|
||||
|
||||
for (;;) {
|
||||
|
@ -790,12 +787,10 @@ bool MovePreservingDestinationInode(const char *from, const char *to) {
|
|||
remain -= rc;
|
||||
} else if (errno == EXDEV || errno == ENOSYS) {
|
||||
if (lseek(fdin, 0, SEEK_SET) == -1) {
|
||||
kprintf("%s: failed to lseek\n", from);
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
if (lseek(fdout, 0, SEEK_SET) == -1) {
|
||||
kprintf("%s: failed to lseek\n", to);
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
|
@ -811,26 +806,6 @@ bool MovePreservingDestinationInode(const char *from, const char *to) {
|
|||
return res;
|
||||
}
|
||||
|
||||
bool IsNativeExecutable(const char *path) {
|
||||
bool res;
|
||||
char buf[4];
|
||||
int got, fd;
|
||||
res = false;
|
||||
if ((fd = open(path, O_RDONLY)) != -1) {
|
||||
if ((got = read(fd, buf, 4)) == 4) {
|
||||
if (IsWindows()) {
|
||||
res = READ16LE(buf) == READ16LE("MZ");
|
||||
} else if (IsXnu()) {
|
||||
res = READ32LE(buf) == 0xFEEDFACEu + 1;
|
||||
} else {
|
||||
res = READ32LE(buf) == READ32LE("\177ELF");
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char *MakeTmpOut(const char *path) {
|
||||
int c;
|
||||
char *p = tmpout;
|
||||
|
@ -840,7 +815,10 @@ char *MakeTmpOut(const char *path) {
|
|||
while ((c = *path++)) {
|
||||
if (c == '/') c = '_';
|
||||
if (p == e) {
|
||||
kprintf("MakeTmpOut path too long: %s\n", tmpout);
|
||||
tinyprint(2, program_invocation_short_name,
|
||||
": fatal error: MakeTmpOut() generated temporary filename "
|
||||
"that's too long: ",
|
||||
tmpout, "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
*p++ = c;
|
||||
|
@ -864,14 +842,12 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
mode = firstnonnull(getenv("MODE"), MODE);
|
||||
|
||||
/*
|
||||
* parse prefix arguments
|
||||
*/
|
||||
// parse prefix arguments
|
||||
verbose = 4;
|
||||
timeout = 90; /* secs */
|
||||
cpuquota = 32; /* secs */
|
||||
proquota = 2048; /* procs */
|
||||
stkquota = 2 * 1024 * 1024; /* bytes */
|
||||
stkquota = 8 * 1024 * 1024; /* bytes */
|
||||
fszquota = 256 * 1000 * 1000; /* bytes */
|
||||
memquota = 512 * 1024 * 1024; /* bytes */
|
||||
if ((s = getenv("V"))) verbose = atoi(s);
|
||||
|
@ -922,24 +898,23 @@ int main(int argc, char *argv[]) {
|
|||
outquota = sizetol(optarg, 1024);
|
||||
break;
|
||||
case 'h':
|
||||
fputs(MANUAL, stdout);
|
||||
tinyprint(1, MANUAL, NULL);
|
||||
exit(0);
|
||||
default:
|
||||
fputs(MANUAL, stderr);
|
||||
tinyprint(2, MANUAL, NULL);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (optind == argc) {
|
||||
fputs("error: missing arguments\n", stderr);
|
||||
tinyprint(2, program_invocation_short_name, ": missing arguments\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* extend limits for slow UBSAN in particular
|
||||
*/
|
||||
// extend limits for slow UBSAN in particular
|
||||
if (!strcmp(mode, "dbg") || !strcmp(mode, "ubsan")) {
|
||||
cpuquota *= 2;
|
||||
fszquota *= 2;
|
||||
stkquota *= 2;
|
||||
memquota *= 2;
|
||||
timeout *= 2;
|
||||
}
|
||||
|
@ -964,18 +939,14 @@ int main(int argc, char *argv[]) {
|
|||
ispkg = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ingest arguments
|
||||
*/
|
||||
// ingest arguments
|
||||
for (i = optind; i < argc; ++i) {
|
||||
|
||||
/*
|
||||
* replace output filename argument
|
||||
*
|
||||
* some commands (e.g. ar) don't use the `-o PATH` notation. in that
|
||||
* case we assume the output path was passed to compile.com -TTARGET
|
||||
* which means we can replace the appropriate command line argument.
|
||||
*/
|
||||
// replace output filename argument
|
||||
//
|
||||
// some commands (e.g. ar) don't use the `-o PATH` notation. in that
|
||||
// case we assume the output path was passed to compile.com -TTARGET
|
||||
// which means we can replace the appropriate command line argument.
|
||||
if (!noworkaround && //
|
||||
!movepath && //
|
||||
!outpath && //
|
||||
|
@ -1232,9 +1203,7 @@ int main(int argc, char *argv[]) {
|
|||
exit(7);
|
||||
}
|
||||
|
||||
/*
|
||||
* append special args
|
||||
*/
|
||||
// append special args
|
||||
if (iscc) {
|
||||
if (isclang) {
|
||||
AddArg("-Wno-unused-command-line-argument");
|
||||
|
@ -1290,9 +1259,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* scrub environment for determinism and great justice
|
||||
*/
|
||||
// scrub environment for determinism and great justice
|
||||
for (envp = environ; *envp; ++envp) {
|
||||
if (startswith(*envp, "MODE=")) {
|
||||
mode = *envp + 5;
|
||||
|
@ -1304,9 +1271,7 @@ int main(int argc, char *argv[]) {
|
|||
AddEnv("LC_ALL=C");
|
||||
AddEnv("SOURCE_DATE_EPOCH=0");
|
||||
|
||||
/*
|
||||
* ensure output directory exists
|
||||
*/
|
||||
// ensure output directory exists
|
||||
if (outpath) {
|
||||
outdir = xdirname(outpath);
|
||||
if (!isdirectory(outdir)) {
|
||||
|
@ -1314,49 +1279,40 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* make sense of standard i/o file descriptors
|
||||
* we want to permit pipelines but prevent talking to terminal
|
||||
*/
|
||||
// make sense of standard i/o file descriptors
|
||||
// we want to permit pipelines but prevent talking to terminal
|
||||
stdoutmustclose = fstat(1, &st) == -1 || S_ISCHR(st.st_mode);
|
||||
if (fstat(0, &st) == -1 || S_ISCHR(st.st_mode)) {
|
||||
close(0);
|
||||
open("/dev/null", O_RDONLY);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGINT (CTRL-C) and SIGQUIT (CTRL-\) are delivered to process group
|
||||
* so the correct thing to do is to do nothing, and wait for the child
|
||||
* to die as a result of those signals. SIGPIPE shouldn't happen until
|
||||
* the very end since we buffer so it is safe to let it kill the prog.
|
||||
* Most importantly we need SIGCHLD to interrupt the read() operation!
|
||||
*/
|
||||
sigfillset(&mask);
|
||||
sigdelset(&mask, SIGILL);
|
||||
sigdelset(&mask, SIGBUS);
|
||||
sigdelset(&mask, SIGPIPE);
|
||||
sigdelset(&mask, SIGALRM);
|
||||
sigdelset(&mask, SIGSEGV);
|
||||
sigdelset(&mask, SIGCHLD);
|
||||
// SIGINT (CTRL-C) and SIGQUIT (CTRL-\) are delivered to the child
|
||||
// process, so we should ignore it and wait for the child to die.
|
||||
// SIGPIPE shouldn't happen until the very end since we buffer so it
|
||||
// is safe to let it kill the prog.
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGQUIT);
|
||||
sigprocmask(SIG_BLOCK, &mask, &savemask);
|
||||
|
||||
// we want SIGCHLD to interrupt the read() operation
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
|
||||
sa.sa_sigaction = OnChld;
|
||||
if (sigaction(SIGCHLD, &sa, 0) == -1) exit(83);
|
||||
sigaction(SIGCHLD, &sa, 0);
|
||||
|
||||
// set a death clock if requested
|
||||
if (timeout > 0) {
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = OnAlrm;
|
||||
sigaction(SIGALRM, &sa, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* run command
|
||||
*/
|
||||
// run command
|
||||
ws = Launch();
|
||||
|
||||
/*
|
||||
* propagate exit
|
||||
*/
|
||||
// propagate exit
|
||||
if (ws != -1) {
|
||||
if (WIFEXITED(ws)) {
|
||||
if (!(exitcode = WEXITSTATUS(ws)) || exitcode == 254) {
|
||||
|
@ -1423,9 +1379,7 @@ int main(int argc, char *argv[]) {
|
|||
exitcode = 89;
|
||||
}
|
||||
|
||||
/*
|
||||
* describe command that was run
|
||||
*/
|
||||
// describe command that was run
|
||||
if (!exitcode || exitcode == 254) {
|
||||
if (exitcode == 254) {
|
||||
exitcode = 0;
|
||||
|
@ -1576,9 +1530,7 @@ int main(int argc, char *argv[]) {
|
|||
ReportResources();
|
||||
}
|
||||
|
||||
/*
|
||||
* flush output
|
||||
*/
|
||||
// flush output
|
||||
if (WriteAllUntilSignalledOrError(2, output, appendz(output).i) == -1) {
|
||||
if (errno == EINTR) {
|
||||
s = "notice: compile.com output truncated\n";
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ TOOL_BUILD_LIB_A_DIRECTDEPS = \
|
|||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_THREAD \
|
||||
LIBC_TIME \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_X \
|
||||
|
|
|
@ -17,11 +17,13 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "tool/build/lib/eztls.h"
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
|
@ -29,12 +31,30 @@
|
|||
#include "third_party/mbedtls/net_sockets.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
|
||||
struct EzTlsBio ezbio;
|
||||
mbedtls_ssl_config ezconf;
|
||||
mbedtls_ssl_context ezssl;
|
||||
mbedtls_ctr_drbg_context ezrng;
|
||||
_Thread_local int mytid;
|
||||
_Thread_local struct EzTlsBio ezbio;
|
||||
_Thread_local mbedtls_ssl_config ezconf;
|
||||
_Thread_local mbedtls_ssl_context ezssl;
|
||||
_Thread_local mbedtls_ctr_drbg_context ezrng;
|
||||
|
||||
void EzSanity(void) {
|
||||
unassert(mytid);
|
||||
unassert(mytid == gettid());
|
||||
}
|
||||
|
||||
void EzTlsDie(const char *s, int r) {
|
||||
EzSanity();
|
||||
if (IsTiny()) {
|
||||
kprintf("error: %s (-0x%04x %s)\n", s, -r, GetTlsError(r));
|
||||
} else {
|
||||
kprintf("error: %s (grep -0x%04x)\n", s, -r);
|
||||
}
|
||||
EzDestroy();
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) {
|
||||
EzSanity();
|
||||
int i;
|
||||
ssize_t rc;
|
||||
size_t wrote, total;
|
||||
|
@ -58,7 +78,7 @@ static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) {
|
|||
}
|
||||
} while (wrote);
|
||||
} else {
|
||||
WARNF("writev() failed %m");
|
||||
// WARNF("writev() failed %m");
|
||||
if (errno != EINTR) {
|
||||
return total ? total : -1;
|
||||
}
|
||||
|
@ -68,6 +88,7 @@ static ssize_t EzWritevAll(int fd, struct iovec *iov, int iovlen) {
|
|||
}
|
||||
|
||||
int EzTlsFlush(struct EzTlsBio *bio, const unsigned char *buf, size_t len) {
|
||||
EzSanity();
|
||||
struct iovec v[2];
|
||||
if (len || bio->c > 0) {
|
||||
v[0].iov_base = bio->u;
|
||||
|
@ -81,7 +102,7 @@ int EzTlsFlush(struct EzTlsBio *bio, const unsigned char *buf, size_t len) {
|
|||
} else if (errno == EPIPE || errno == ECONNRESET || errno == ENETRESET) {
|
||||
return MBEDTLS_ERR_NET_CONN_RESET;
|
||||
} else {
|
||||
WARNF("EzTlsSend error %s", strerror(errno));
|
||||
// WARNF("EzTlsSend error %s", strerror(errno));
|
||||
return MBEDTLS_ERR_NET_SEND_FAILED;
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +110,7 @@ int EzTlsFlush(struct EzTlsBio *bio, const unsigned char *buf, size_t len) {
|
|||
}
|
||||
|
||||
static int EzTlsSend(void *ctx, const unsigned char *buf, size_t len) {
|
||||
EzSanity();
|
||||
int rc;
|
||||
struct EzTlsBio *bio = ctx;
|
||||
if (bio->c >= 0 && bio->c + len <= sizeof(bio->u)) {
|
||||
|
@ -101,6 +123,7 @@ static int EzTlsSend(void *ctx, const unsigned char *buf, size_t len) {
|
|||
}
|
||||
|
||||
static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
|
||||
EzSanity();
|
||||
int r;
|
||||
struct iovec v[2];
|
||||
struct EzTlsBio *bio = ctx;
|
||||
|
@ -116,7 +139,7 @@ static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
|
|||
v[1].iov_base = bio->t;
|
||||
v[1].iov_len = sizeof(bio->t);
|
||||
while ((r = readv(bio->fd, v, 2)) == -1) {
|
||||
WARNF("tls read() error %s", strerror(errno));
|
||||
// WARNF("tls read() error %s", strerror(errno));
|
||||
if (errno == EINTR) {
|
||||
return MBEDTLS_ERR_SSL_WANT_READ;
|
||||
} else if (errno == EAGAIN) {
|
||||
|
@ -132,60 +155,79 @@ static int EzTlsRecvImpl(void *ctx, unsigned char *p, size_t n, uint32_t o) {
|
|||
}
|
||||
|
||||
static int EzTlsRecv(void *ctx, unsigned char *buf, size_t len, uint32_t tmo) {
|
||||
EzSanity();
|
||||
return EzTlsRecvImpl(ctx, buf, len, tmo);
|
||||
}
|
||||
|
||||
void EzFd(int fd) {
|
||||
EzSanity();
|
||||
mbedtls_ssl_session_reset(&ezssl);
|
||||
mbedtls_platform_zeroize(&ezbio, sizeof(ezbio));
|
||||
ezbio.fd = fd;
|
||||
}
|
||||
|
||||
void EzHandshake(void) {
|
||||
EzSanity();
|
||||
int rc;
|
||||
while ((rc = mbedtls_ssl_handshake(&ezssl))) {
|
||||
if (rc != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
TlsDie("handshake failed", rc);
|
||||
EzTlsDie("handshake failed", rc);
|
||||
}
|
||||
}
|
||||
while ((rc = EzTlsFlush(&ezbio, 0, 0))) {
|
||||
if (rc != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
TlsDie("handshake flush failed", rc);
|
||||
EzTlsDie("handshake flush failed", rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int EzHandshake2(void) {
|
||||
EzSanity();
|
||||
int rc;
|
||||
while ((rc = mbedtls_ssl_handshake(&ezssl))) {
|
||||
if (rc == MBEDTLS_ERR_NET_CONN_RESET) {
|
||||
return rc;
|
||||
} else if (rc != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
TlsDie("handshake failed", rc);
|
||||
EzTlsDie("handshake failed", rc);
|
||||
}
|
||||
}
|
||||
while ((rc = EzTlsFlush(&ezbio, 0, 0))) {
|
||||
if (rc == MBEDTLS_ERR_NET_CONN_RESET) {
|
||||
return rc;
|
||||
} else if (rc != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
TlsDie("handshake flush failed", rc);
|
||||
EzTlsDie("handshake flush failed", rc);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EzInitialize(void) {
|
||||
xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0);
|
||||
ezconf.disable_compression = 1; /* TODO(jart): Why does it behave weirdly? */
|
||||
unassert(!mytid);
|
||||
mytid = gettid();
|
||||
mbedtls_ssl_init(&ezssl);
|
||||
mbedtls_ssl_config_init(&ezconf);
|
||||
mbedtls_platform_zeroize(&ezbio, sizeof(ezbio));
|
||||
ezconf.disable_compression = 1;
|
||||
InitializeRng(&ezrng);
|
||||
}
|
||||
|
||||
void EzSetup(char psk[32]) {
|
||||
int rc;
|
||||
EzSanity();
|
||||
mbedtls_ssl_conf_rng(&ezconf, mbedtls_ctr_drbg_random, &ezrng);
|
||||
if ((rc = mbedtls_ssl_conf_psk(&ezconf, psk, 32, "runit", 5)) ||
|
||||
(rc = mbedtls_ssl_setup(&ezssl, &ezconf))) {
|
||||
TlsDie("EzSetup", rc);
|
||||
if ((rc = mbedtls_ssl_conf_psk(&ezconf, psk, 32, "runit", 5))) {
|
||||
EzTlsDie("EzSetup mbedtls_ssl_conf_psk", rc);
|
||||
}
|
||||
if ((rc = mbedtls_ssl_setup(&ezssl, &ezconf))) {
|
||||
EzTlsDie("EzSetup mbedtls_ssl_setup", rc);
|
||||
}
|
||||
mbedtls_ssl_set_bio(&ezssl, &ezbio, EzTlsSend, 0, EzTlsRecv);
|
||||
}
|
||||
|
||||
void EzDestroy(void) {
|
||||
if (!mytid) return;
|
||||
EzSanity();
|
||||
mbedtls_ssl_free(&ezssl);
|
||||
mbedtls_ctr_drbg_free(&ezrng);
|
||||
mbedtls_ssl_config_free(&ezconf);
|
||||
mytid = 0;
|
||||
}
|
||||
|
|
|
@ -13,16 +13,19 @@ struct EzTlsBio {
|
|||
unsigned char u[1430];
|
||||
};
|
||||
|
||||
extern struct EzTlsBio ezbio;
|
||||
extern mbedtls_ssl_config ezconf;
|
||||
extern mbedtls_ssl_context ezssl;
|
||||
extern mbedtls_ctr_drbg_context ezrng;
|
||||
extern _Thread_local struct EzTlsBio ezbio;
|
||||
extern _Thread_local mbedtls_ssl_config ezconf;
|
||||
extern _Thread_local mbedtls_ssl_context ezssl;
|
||||
extern _Thread_local mbedtls_ctr_drbg_context ezrng;
|
||||
|
||||
void EzFd(int);
|
||||
void EzSanity(void);
|
||||
void EzDestroy(void);
|
||||
void EzHandshake(void);
|
||||
int EzHandshake2(void);
|
||||
void EzSetup(char[32]);
|
||||
void EzInitialize(void);
|
||||
void EzTlsDie(const char *, int);
|
||||
int EzTlsFlush(struct EzTlsBio *, const unsigned char *, size_t);
|
||||
|
||||
/*
|
||||
|
|
|
@ -27,11 +27,14 @@
|
|||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sock/ipclassify.internal.h"
|
||||
|
@ -50,6 +53,7 @@
|
|||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
#include "net/https/https.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
#include "third_party/zlib/zlib.h"
|
||||
|
@ -271,64 +275,74 @@ void RelayRequest(void) {
|
|||
for (i = 0; i < have; i += rc) {
|
||||
rc = mbedtls_ssl_write(&ezssl, buf + i, have - i);
|
||||
if (rc <= 0) {
|
||||
TlsDie("relay request failed", rc);
|
||||
EzTlsDie("relay request failed", rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
CHECK_NE(0, transferred);
|
||||
rc = EzTlsFlush(&ezbio, 0, 0);
|
||||
if (rc < 0) {
|
||||
TlsDie("relay request failed to flush", rc);
|
||||
EzTlsDie("relay request failed to flush", rc);
|
||||
}
|
||||
close(13);
|
||||
}
|
||||
|
||||
bool Recv(unsigned char *p, size_t n) {
|
||||
size_t i, rc;
|
||||
bool Recv(char *p, int n) {
|
||||
int i, rc;
|
||||
for (i = 0; i < n; i += rc) {
|
||||
do {
|
||||
rc = mbedtls_ssl_read(&ezssl, p + i, n - i);
|
||||
} while (rc == MBEDTLS_ERR_SSL_WANT_READ);
|
||||
do rc = mbedtls_ssl_read(&ezssl, p + i, n - i);
|
||||
while (rc == MBEDTLS_ERR_SSL_WANT_READ);
|
||||
if (!rc) return false;
|
||||
if (rc < 0) {
|
||||
TlsDie("read response failed", rc);
|
||||
}
|
||||
if (rc < 0) EzTlsDie("read response failed", rc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int ReadResponse(void) {
|
||||
int res;
|
||||
size_t n;
|
||||
uint32_t size;
|
||||
unsigned char b[512];
|
||||
for (res = -1; res == -1;) {
|
||||
if (!Recv(b, 5)) break;
|
||||
CHECK_EQ(RUNITD_MAGIC, READ32BE(b), "%#.5s", b);
|
||||
switch (b[4]) {
|
||||
case kRunitExit:
|
||||
if (!Recv(b, 1)) break;
|
||||
if ((res = *b)) {
|
||||
WARNF("%s on %s exited with %d", g_prog, g_hostname, res);
|
||||
}
|
||||
int exitcode;
|
||||
for (;;) {
|
||||
char msg[5];
|
||||
if (!Recv(msg, 5)) {
|
||||
WARNF("%s didn't report status of %s", g_hostname, g_prog);
|
||||
exitcode = 200;
|
||||
break;
|
||||
}
|
||||
if (READ32BE(msg) != RUNITD_MAGIC) {
|
||||
WARNF("%s sent corrupted data stream after running %s", g_hostname,
|
||||
g_prog);
|
||||
exitcode = 201;
|
||||
break;
|
||||
}
|
||||
if (msg[4] == kRunitExit) {
|
||||
if (!Recv(msg, 1)) {
|
||||
TruncatedMessage:
|
||||
WARNF("%s sent truncated message running %s", g_hostname, g_prog);
|
||||
exitcode = 202;
|
||||
break;
|
||||
case kRunitStderr:
|
||||
if (!Recv(b, 4)) break;
|
||||
size = READ32BE(b);
|
||||
for (; size; size -= n) {
|
||||
n = MIN(size, sizeof(b));
|
||||
if (!Recv(b, n)) goto drop;
|
||||
CHECK_EQ(n, write(2, b, n));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "error: received invalid runit command\n");
|
||||
_exit(1);
|
||||
}
|
||||
exitcode = *msg;
|
||||
if (exitcode) {
|
||||
WARNF("%s says %s exited with %d", g_hostname, g_prog, exitcode);
|
||||
} else {
|
||||
VERBOSEF("%s says %s exited with %d", g_hostname, g_prog, exitcode);
|
||||
}
|
||||
mbedtls_ssl_close_notify(&ezssl);
|
||||
break;
|
||||
} else if (msg[4] == kRunitStdout || msg[4] == kRunitStderr) {
|
||||
if (!Recv(msg, 4)) goto TruncatedMessage;
|
||||
int n = READ32BE(msg);
|
||||
char *s = malloc(n);
|
||||
if (!Recv(s, n)) goto TruncatedMessage;
|
||||
write(2, s, n);
|
||||
free(s);
|
||||
} else {
|
||||
WARNF("%s sent message with unknown command %d after running %s",
|
||||
g_hostname, msg[4], g_prog);
|
||||
exitcode = 203;
|
||||
break;
|
||||
}
|
||||
}
|
||||
drop:
|
||||
close(g_sock);
|
||||
return res;
|
||||
return exitcode;
|
||||
}
|
||||
|
||||
static inline bool IsElf(const char *p, size_t n) {
|
||||
|
@ -340,23 +354,28 @@ static inline bool IsMachO(const char *p, size_t n) {
|
|||
}
|
||||
|
||||
int RunOnHost(char *spec) {
|
||||
int rc;
|
||||
int err;
|
||||
char *p;
|
||||
for (p = spec; *p; ++p) {
|
||||
if (*p == ':') *p = ' ';
|
||||
}
|
||||
CHECK_GE(sscanf(spec, "%100s %hu %hu", g_hostname, &g_runitdport, &g_sshport),
|
||||
1);
|
||||
int got =
|
||||
sscanf(spec, "%100s %hu %hu", g_hostname, &g_runitdport, &g_sshport);
|
||||
if (got < 1) {
|
||||
kprintf("what on earth %#s -> %d\n", spec, got);
|
||||
exit(1);
|
||||
}
|
||||
if (!strchr(g_hostname, '.')) strcat(g_hostname, ".test.");
|
||||
DEBUGF("connecting to %s port %d", g_hostname, g_runitdport);
|
||||
for (;;) {
|
||||
Connect();
|
||||
EzFd(g_sock);
|
||||
if (!(rc = EzHandshake2())) {
|
||||
break;
|
||||
}
|
||||
WARNF("got reset in handshake -0x%04x", rc);
|
||||
err = EzHandshake2();
|
||||
if (!err) break;
|
||||
WARNF("handshake with %s:%d failed -0x%04x (%s)", //
|
||||
g_hostname, g_runitdport, err, GetTlsError(err));
|
||||
close(g_sock);
|
||||
return 1;
|
||||
}
|
||||
RelayRequest();
|
||||
return ReadResponse();
|
||||
|
@ -454,6 +473,7 @@ int SpawnSubprocesses(int argc, char *argv[]) {
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
ShowCrashReports();
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
if (getenv("DEBUG")) {
|
||||
__log_level = kLogDebug;
|
||||
}
|
||||
|
|
|
@ -16,8 +16,12 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/atomic.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
|
@ -26,19 +30,27 @@
|
|||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/log/appendresourcereport.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/gc.h"
|
||||
#include "libc/mem/gc.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/crc32.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/syslib.internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sock/struct/pollfd.h"
|
||||
#include "libc/sock/struct/sockaddr.h"
|
||||
#include "libc/stdio/append.h"
|
||||
#include "libc/stdio/posix_spawn.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/af.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/clock.h"
|
||||
#include "libc/sysv/consts/ex.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
|
@ -48,15 +60,21 @@
|
|||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/posix.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/so.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
#include "libc/sysv/consts/sol.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/temp.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/thread2.h"
|
||||
#include "libc/time/struct/tm.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "libc/x/xasprintf.h"
|
||||
#include "libc/x/xsigaction.h"
|
||||
#include "net/http/escape.h"
|
||||
#include "net/https/https.h"
|
||||
#include "third_party/getopt/getopt.internal.h"
|
||||
#include "third_party/mbedtls/ssl.h"
|
||||
|
@ -104,45 +122,64 @@
|
|||
#define kLogFile "o/runitd.log"
|
||||
#define kLogMaxBytes (2 * 1000 * 1000)
|
||||
|
||||
#define LOG_LEVEL_WARN 0
|
||||
#define LOG_LEVEL_INFO 1
|
||||
#define LOG_LEVEL_VERB 3
|
||||
#define LOG_LEVEL_DEBU 3
|
||||
|
||||
#define DEBUF(FMT, ...) LOGF(DEBU, FMT, ##__VA_ARGS__)
|
||||
#define VERBF(FMT, ...) LOGF(VERB, FMT, ##__VA_ARGS__)
|
||||
#define INFOF(FMT, ...) LOGF(INFO, FMT, ##__VA_ARGS__)
|
||||
#define WARNF(FMT, ...) LOGF(WARN, FMT, ##__VA_ARGS__)
|
||||
|
||||
#define LOGF(LVL, FMT, ...) \
|
||||
do { \
|
||||
if (g_log_level >= LOG_LEVEL_##LVL) { \
|
||||
kprintf("%r" #LVL " %6P %'18T %s:%d " FMT "\n", __FILE__, __LINE__, \
|
||||
##__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct Client {
|
||||
int fd;
|
||||
int pid;
|
||||
int pipe[2];
|
||||
pthread_t th;
|
||||
uint32_t addrsize;
|
||||
struct sockaddr_in addr;
|
||||
bool once;
|
||||
int zstatus;
|
||||
z_stream zs;
|
||||
struct {
|
||||
size_t off;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
char *data;
|
||||
} rbuf;
|
||||
char *output;
|
||||
char exepath[128];
|
||||
char buf[32768];
|
||||
};
|
||||
|
||||
char *g_psk;
|
||||
int g_log_level;
|
||||
bool use_ftrace;
|
||||
bool use_strace;
|
||||
char *g_exepath;
|
||||
unsigned char g_buf[4096];
|
||||
volatile bool g_interrupted;
|
||||
char g_hostname[256];
|
||||
int g_bogusfd, g_servfd;
|
||||
atomic_bool g_interrupted;
|
||||
struct sockaddr_in g_servaddr;
|
||||
bool g_daemonize, g_sendready;
|
||||
int g_timeout, g_bogusfd, g_servfd, g_clifd, g_exefd;
|
||||
|
||||
void OnInterrupt(int sig) {
|
||||
g_interrupted = true;
|
||||
}
|
||||
|
||||
void OnChildTerminated(int sig) {
|
||||
int e, ws, pid;
|
||||
sigset_t ss, oldss;
|
||||
e = errno; // SIGCHLD can be called asynchronously
|
||||
sigfillset(&ss);
|
||||
sigdelset(&ss, SIGTERM);
|
||||
sigprocmask(SIG_BLOCK, &ss, &oldss);
|
||||
for (;;) {
|
||||
if ((pid = waitpid(-1, &ws, WNOHANG)) != -1) {
|
||||
if (pid) {
|
||||
if (WIFEXITED(ws)) {
|
||||
DEBUGF("worker %d exited with %d", pid, WEXITSTATUS(ws));
|
||||
} else {
|
||||
DEBUGF("worker %d terminated with %s", pid, strsignal(WTERMSIG(ws)));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == ECHILD) break;
|
||||
FATALF("waitpid failed in sigchld");
|
||||
}
|
||||
void Close(int *fd) {
|
||||
if (*fd > 0) {
|
||||
close(*fd);
|
||||
*fd = -1; // poll ignores -1
|
||||
}
|
||||
sigprocmask(SIG_SETMASK, &oldss, 0);
|
||||
errno = e;
|
||||
}
|
||||
|
||||
wontreturn void ShowUsage(FILE *f, int rc) {
|
||||
|
@ -151,9 +188,18 @@ wontreturn void ShowUsage(FILE *f, int rc) {
|
|||
exit(rc);
|
||||
}
|
||||
|
||||
char *DescribeAddress(struct sockaddr_in *addr) {
|
||||
static _Thread_local char res[64];
|
||||
char ip4buf[64];
|
||||
sprintf(res, "%s:%hu",
|
||||
inet_ntop(addr->sin_family, &addr->sin_addr.s_addr, ip4buf,
|
||||
sizeof(ip4buf)),
|
||||
ntohs(addr->sin_port));
|
||||
return res;
|
||||
}
|
||||
|
||||
void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
g_timeout = RUNITD_TIMEOUT_MS;
|
||||
g_servaddr.sin_family = AF_INET;
|
||||
g_servaddr.sin_port = htons(RUNITD_PORT);
|
||||
g_servaddr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
@ -166,10 +212,10 @@ void GetOpts(int argc, char *argv[]) {
|
|||
use_strace = true;
|
||||
break;
|
||||
case 'q':
|
||||
--__log_level;
|
||||
--g_log_level;
|
||||
break;
|
||||
case 'v':
|
||||
++__log_level;
|
||||
++g_log_level;
|
||||
break;
|
||||
case 'd':
|
||||
g_daemonize = true;
|
||||
|
@ -178,56 +224,44 @@ void GetOpts(int argc, char *argv[]) {
|
|||
g_sendready = true;
|
||||
break;
|
||||
case 't':
|
||||
g_timeout = atoi(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
CHECK_NE(0xFFFF, (g_servaddr.sin_port = htons(parseport(optarg))));
|
||||
g_servaddr.sin_port = htons(parseport(optarg));
|
||||
break;
|
||||
case 'l':
|
||||
CHECK_EQ(1, inet_pton(AF_INET, optarg, &g_servaddr.sin_addr));
|
||||
inet_pton(AF_INET, optarg, &g_servaddr.sin_addr);
|
||||
break;
|
||||
case 'h':
|
||||
ShowUsage(stdout, EXIT_SUCCESS);
|
||||
__builtin_unreachable();
|
||||
default:
|
||||
ShowUsage(stderr, EX_USAGE);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__wur char *DescribeAddress(struct sockaddr_in *addr) {
|
||||
char ip4buf[16];
|
||||
return xasprintf("%s:%hu",
|
||||
inet_ntop(addr->sin_family, &addr->sin_addr.s_addr, ip4buf,
|
||||
sizeof(ip4buf)),
|
||||
ntohs(addr->sin_port));
|
||||
}
|
||||
|
||||
void StartTcpServer(void) {
|
||||
int yes = true;
|
||||
uint32_t asize;
|
||||
|
||||
/*
|
||||
* TODO: How can we make close(serversocket) on Windows go fast?
|
||||
* That way we can put back SOCK_CLOEXEC.
|
||||
*/
|
||||
CHECK_NE(-1, (g_servfd =
|
||||
socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP)));
|
||||
|
||||
struct timeval timeo = {30};
|
||||
g_servfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
|
||||
if (g_servfd == -1) {
|
||||
fprintf(stderr, program_invocation_short_name,
|
||||
": socket failed: ", strerror(errno), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
struct timeval timeo = {DEATH_CLOCK_SECONDS / 10};
|
||||
setsockopt(g_servfd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
|
||||
setsockopt(g_servfd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
|
||||
|
||||
LOGIFNEG1(setsockopt(g_servfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)));
|
||||
setsockopt(g_servfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
if (bind(g_servfd, (struct sockaddr *)&g_servaddr, sizeof(g_servaddr)) ==
|
||||
-1) {
|
||||
FATALF("bind failed %m");
|
||||
fprintf(stderr, program_invocation_short_name,
|
||||
": bind failed: ", strerror(errno), "\n", NULL);
|
||||
exit(1);
|
||||
}
|
||||
CHECK_NE(-1, listen(g_servfd, 10));
|
||||
unassert(!listen(g_servfd, 10));
|
||||
asize = sizeof(g_servaddr);
|
||||
CHECK_NE(-1, getsockname(g_servfd, (struct sockaddr *)&g_servaddr, &asize));
|
||||
INFOF("%s:%s", "listening on tcp", _gc(DescribeAddress(&g_servaddr)));
|
||||
unassert(!getsockname(g_servfd, (struct sockaddr *)&g_servaddr, &asize));
|
||||
INFOF("listening on tcp:%s", DescribeAddress(&g_servaddr));
|
||||
if (g_sendready) {
|
||||
printf("ready %hu\n", ntohs(g_servaddr.sin_port));
|
||||
fflush(stdout);
|
||||
|
@ -237,22 +271,28 @@ void StartTcpServer(void) {
|
|||
}
|
||||
|
||||
void SendExitMessage(int rc) {
|
||||
EzSanity();
|
||||
int res;
|
||||
unsigned char msg[4 + 1 + 1];
|
||||
DEBUF("SendExitMessage");
|
||||
msg[0 + 0] = (RUNITD_MAGIC & 0xff000000) >> 030;
|
||||
msg[0 + 1] = (RUNITD_MAGIC & 0x00ff0000) >> 020;
|
||||
msg[0 + 2] = (RUNITD_MAGIC & 0x0000ff00) >> 010;
|
||||
msg[0 + 3] = (RUNITD_MAGIC & 0x000000ff) >> 000;
|
||||
msg[4] = kRunitExit;
|
||||
msg[5] = rc;
|
||||
INFOF("mbedtls_ssl_write");
|
||||
CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg)));
|
||||
CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0));
|
||||
DEBUF("mbedtls_ssl_write");
|
||||
if (sizeof(msg) != (res = mbedtls_ssl_write(&ezssl, msg, sizeof(msg)))) {
|
||||
EzTlsDie("SendExitMessage mbedtls_ssl_write failed", res);
|
||||
}
|
||||
if ((res = EzTlsFlush(&ezbio, 0, 0))) {
|
||||
EzTlsDie("SendExitMessage EzTlsFlush failed", res);
|
||||
}
|
||||
}
|
||||
|
||||
void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf,
|
||||
size_t size) {
|
||||
void SendOutputFragmentMessage(enum RunitCommand kind, char *buf, size_t size) {
|
||||
EzSanity();
|
||||
ssize_t rc;
|
||||
size_t sent;
|
||||
unsigned char msg[4 + 1 + 4];
|
||||
msg[0 + 0] = (RUNITD_MAGIC & 0xff000000) >> 030;
|
||||
msg[0 + 1] = (RUNITD_MAGIC & 0x00ff0000) >> 020;
|
||||
|
@ -263,309 +303,451 @@ void SendOutputFragmentMessage(enum RunitCommand kind, unsigned char *buf,
|
|||
msg[5 + 1] = (size & 0x00ff0000) >> 020;
|
||||
msg[5 + 2] = (size & 0x0000ff00) >> 010;
|
||||
msg[5 + 3] = (size & 0x000000ff) >> 000;
|
||||
INFOF("mbedtls_ssl_write");
|
||||
CHECK_EQ(sizeof(msg), mbedtls_ssl_write(&ezssl, msg, sizeof(msg)));
|
||||
while (size) {
|
||||
CHECK_NE(-1, (rc = mbedtls_ssl_write(&ezssl, buf, size)));
|
||||
CHECK_LE((sent = (size_t)rc), size);
|
||||
size -= sent;
|
||||
buf += sent;
|
||||
DEBUF("mbedtls_ssl_write");
|
||||
if (sizeof(msg) != (rc = mbedtls_ssl_write(&ezssl, msg, sizeof(msg)))) {
|
||||
EzTlsDie("SendOutputFragmentMessage mbedtls_ssl_write failed", rc);
|
||||
}
|
||||
while (size) {
|
||||
if ((rc = mbedtls_ssl_write(&ezssl, buf, size)) <= 0) {
|
||||
EzTlsDie("SendOutputFragmentMessage mbedtls_ssl_write #2 failed", rc);
|
||||
}
|
||||
size -= rc;
|
||||
buf += rc;
|
||||
}
|
||||
if ((rc = EzTlsFlush(&ezbio, 0, 0))) {
|
||||
EzTlsDie("SendOutputFragmentMessage EzTlsFlush failed", rc);
|
||||
}
|
||||
CHECK_EQ(0, EzTlsFlush(&ezbio, 0, 0));
|
||||
}
|
||||
|
||||
void Recv(void *output, size_t outputsize) {
|
||||
void Recv(struct Client *client, void *output, size_t outputsize) {
|
||||
EzSanity();
|
||||
ssize_t chunk, received, totalgot;
|
||||
static bool once;
|
||||
static int zstatus;
|
||||
static char buf[32768];
|
||||
static z_stream zs;
|
||||
static struct {
|
||||
size_t off;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
char *data;
|
||||
} rbuf;
|
||||
if (!once) {
|
||||
CHECK_EQ(Z_OK, inflateInit(&zs));
|
||||
once = true;
|
||||
if (!client->once) {
|
||||
unassert(Z_OK == inflateInit(&client->zs));
|
||||
client->once = true;
|
||||
}
|
||||
totalgot = 0;
|
||||
for (;;) {
|
||||
if (rbuf.len >= outputsize) {
|
||||
memcpy(output, rbuf.data + rbuf.off, outputsize);
|
||||
rbuf.len -= outputsize;
|
||||
rbuf.off += outputsize;
|
||||
if (client->rbuf.len >= outputsize) {
|
||||
memcpy(output, client->rbuf.data + client->rbuf.off, outputsize);
|
||||
client->rbuf.len -= outputsize;
|
||||
client->rbuf.off += outputsize;
|
||||
// trim dymanic buffer once it empties
|
||||
if (!rbuf.len) {
|
||||
rbuf.off = 0;
|
||||
rbuf.cap = 4096;
|
||||
rbuf.data = realloc(rbuf.data, rbuf.cap);
|
||||
if (!client->rbuf.len) {
|
||||
client->rbuf.off = 0;
|
||||
client->rbuf.cap = 4096;
|
||||
client->rbuf.data = realloc(client->rbuf.data, client->rbuf.cap);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (zstatus == Z_STREAM_END) {
|
||||
close(g_clifd);
|
||||
FATALF("recv zlib unexpected eof");
|
||||
if (client->zstatus == Z_STREAM_END) {
|
||||
WARNF("recv zlib unexpected eof");
|
||||
pthread_exit(0);
|
||||
}
|
||||
// get another fixed-size data packet from network
|
||||
// pass along error conditions to caller
|
||||
// pass along eof condition to zlib
|
||||
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
|
||||
received = mbedtls_ssl_read(&ezssl, client->buf, sizeof(client->buf));
|
||||
if (!received) {
|
||||
close(g_clifd);
|
||||
TlsDie("got unexpected eof", received);
|
||||
EzTlsDie("got unexpected eof", received);
|
||||
}
|
||||
if (received < 0) {
|
||||
close(g_clifd);
|
||||
TlsDie("read failed", received);
|
||||
EzTlsDie("read failed", received);
|
||||
}
|
||||
totalgot += received;
|
||||
// decompress packet completely
|
||||
// into a dynamical size buffer
|
||||
zs.avail_in = received;
|
||||
zs.next_in = (unsigned char *)buf;
|
||||
CHECK_EQ(Z_OK, zstatus);
|
||||
client->zs.avail_in = received;
|
||||
client->zs.next_in = (unsigned char *)client->buf;
|
||||
unassert(Z_OK == client->zstatus);
|
||||
do {
|
||||
// make sure we have a reasonable capacity for zlib output
|
||||
if (rbuf.cap - (rbuf.off + rbuf.len) < sizeof(buf)) {
|
||||
rbuf.cap += sizeof(buf);
|
||||
rbuf.data = realloc(rbuf.data, rbuf.cap);
|
||||
if (client->rbuf.cap - (client->rbuf.off + client->rbuf.len) <
|
||||
sizeof(client->buf)) {
|
||||
client->rbuf.cap += sizeof(client->buf);
|
||||
client->rbuf.data = realloc(client->rbuf.data, client->rbuf.cap);
|
||||
}
|
||||
// inflate packet, which naturally can be much larger
|
||||
// permit zlib no delay flushes that come from sender
|
||||
zs.next_out = (unsigned char *)rbuf.data + (rbuf.off + rbuf.len);
|
||||
zs.avail_out = chunk = rbuf.cap - (rbuf.off + rbuf.len);
|
||||
zstatus = inflate(&zs, Z_SYNC_FLUSH);
|
||||
CHECK_NE(Z_STREAM_ERROR, zstatus);
|
||||
switch (zstatus) {
|
||||
client->zs.next_out = (unsigned char *)client->rbuf.data +
|
||||
(client->rbuf.off + client->rbuf.len);
|
||||
client->zs.avail_out = chunk =
|
||||
client->rbuf.cap - (client->rbuf.off + client->rbuf.len);
|
||||
client->zstatus = inflate(&client->zs, Z_SYNC_FLUSH);
|
||||
unassert(Z_STREAM_ERROR != client->zstatus);
|
||||
switch (client->zstatus) {
|
||||
case Z_NEED_DICT:
|
||||
WARNF("tls recv Z_NEED_DICT %ld total %ld", received, totalgot);
|
||||
exit(1);
|
||||
pthread_exit(0);
|
||||
case Z_DATA_ERROR:
|
||||
WARNF("tls recv Z_DATA_ERROR %ld total %ld", received, totalgot);
|
||||
exit(1);
|
||||
pthread_exit(0);
|
||||
case Z_MEM_ERROR:
|
||||
WARNF("tls recv Z_MEM_ERROR %ld total %ld", received, totalgot);
|
||||
exit(1);
|
||||
pthread_exit(0);
|
||||
case Z_BUF_ERROR:
|
||||
zstatus = Z_OK; // harmless? nothing for inflate to do
|
||||
break; // it probably just our wraparound eof
|
||||
client->zstatus = Z_OK; // harmless? nothing for inflate to do
|
||||
break; // it probably just our wraparound eof
|
||||
default:
|
||||
rbuf.len += chunk - zs.avail_out;
|
||||
client->rbuf.len += chunk - client->zs.avail_out;
|
||||
break;
|
||||
}
|
||||
} while (!zs.avail_out);
|
||||
} while (!client->zs.avail_out);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleClient(void) {
|
||||
ssize_t got;
|
||||
void SendProgramOutut(struct Client *client) {
|
||||
if (client->output) {
|
||||
SendOutputFragmentMessage(kRunitStderr, client->output,
|
||||
appendz(client->output).i);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintProgramOutput(struct Client *client) {
|
||||
if (client->output) {
|
||||
char *p = client->output;
|
||||
size_t z = appendz(p).i;
|
||||
if ((p = IndentLines(p, z, &z, 2))) {
|
||||
fwrite(p, 1, z, stderr);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreeClient(struct Client *client) {
|
||||
DEBUF("FreeClient");
|
||||
if (client->pid) {
|
||||
kill(client->pid, SIGHUP);
|
||||
waitpid(client->pid, 0, 0);
|
||||
}
|
||||
Close(&client->fd);
|
||||
if (*client->exepath) {
|
||||
unlink(client->exepath);
|
||||
}
|
||||
if (client->once) {
|
||||
inflateEnd(&client->zs);
|
||||
}
|
||||
EzDestroy();
|
||||
free(client->rbuf.data);
|
||||
free(client->output);
|
||||
free(client);
|
||||
VERBF("---------------");
|
||||
}
|
||||
|
||||
void *ClientWorker(void *arg) {
|
||||
uint32_t crc;
|
||||
sigset_t sigmask;
|
||||
struct sockaddr_in addr;
|
||||
struct timespec now, deadline;
|
||||
int events, wstatus;
|
||||
struct Client *client = arg;
|
||||
uint32_t namesize, filesize;
|
||||
char *addrstr, *exename, *exe;
|
||||
unsigned char msg[4 + 1 + 4 + 4 + 4];
|
||||
uint32_t addrsize, namesize, filesize;
|
||||
int events, exitcode, wstatus, child, pipefds[2];
|
||||
|
||||
/* read request to run program */
|
||||
addrsize = sizeof(addr);
|
||||
INFOF("accept");
|
||||
do {
|
||||
g_clifd =
|
||||
accept4(g_servfd, (struct sockaddr *)&addr, &addrsize, SOCK_CLOEXEC);
|
||||
} while (g_clifd == -1 && errno == EAGAIN);
|
||||
CHECK_NE(-1, g_clifd);
|
||||
if (fork()) {
|
||||
close(g_clifd);
|
||||
return;
|
||||
}
|
||||
EzFd(g_clifd);
|
||||
INFOF("EzHandshake");
|
||||
SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, g_psk);
|
||||
defer(FreeClient, client);
|
||||
|
||||
// read request to run program
|
||||
EzFd(client->fd);
|
||||
DEBUF("EzHandshake");
|
||||
EzHandshake();
|
||||
addrstr = _gc(DescribeAddress(&addr));
|
||||
DEBUGF("%s %s %s", _gc(DescribeAddress(&g_servaddr)), "accepted", addrstr);
|
||||
addrstr = DescribeAddress(&client->addr);
|
||||
DEBUF("%s %s %s", DescribeAddress(&g_servaddr), "accepted", addrstr);
|
||||
|
||||
Recv(msg, sizeof(msg));
|
||||
CHECK_EQ(RUNITD_MAGIC, READ32BE(msg));
|
||||
CHECK_EQ(kRunitExecute, msg[4]);
|
||||
// get the executable
|
||||
Recv(client, msg, sizeof(msg));
|
||||
if (READ32BE(msg) != RUNITD_MAGIC) {
|
||||
WARNF("%s magic mismatch!", addrstr);
|
||||
pthread_exit(0);
|
||||
}
|
||||
if (msg[4] != kRunitExecute) {
|
||||
WARNF("%s unknown command!", addrstr);
|
||||
pthread_exit(0);
|
||||
}
|
||||
namesize = READ32BE(msg + 5);
|
||||
filesize = READ32BE(msg + 9);
|
||||
crc = READ32BE(msg + 13);
|
||||
exename = _gc(calloc(1, namesize + 1));
|
||||
Recv(exename, namesize);
|
||||
g_exepath = _gc(xasprintf("o/%d.%s", getpid(), basename(exename)));
|
||||
INFOF("%s asked we run %`'s (%,u bytes @ %`'s)", addrstr, exename, filesize,
|
||||
g_exepath);
|
||||
|
||||
exe = malloc(filesize);
|
||||
Recv(exe, filesize);
|
||||
exename = gc(calloc(1, namesize + 1));
|
||||
Recv(client, exename, namesize);
|
||||
INFOF("%s sent %#s (%'u bytes @ %#s)", addrstr, exename, filesize,
|
||||
client->exepath);
|
||||
exe = gc(malloc(filesize));
|
||||
Recv(client, exe, filesize);
|
||||
if (crc32_z(0, exe, filesize) != crc) {
|
||||
FATALF("%s crc mismatch! %`'s", addrstr, exename);
|
||||
WARNF("%s crc mismatch! %#s", addrstr, exename);
|
||||
pthread_exit(0);
|
||||
}
|
||||
CHECK_NE(-1, (g_exefd = creat(g_exepath, 0700)));
|
||||
LOGIFNEG1(ftruncate(g_exefd, filesize));
|
||||
CHECK_NE(-1, xwrite(g_exefd, exe, filesize));
|
||||
LOGIFNEG1(close(g_exefd));
|
||||
|
||||
/* run program, tee'ing stderr to both log and client */
|
||||
DEBUGF("spawning %s", exename);
|
||||
// create the executable file
|
||||
// if another thread vforks while we're writing it then a race
|
||||
// condition can happen, where etxtbsy is raised by our execve
|
||||
// we're using o_cloexec so it's guaranteed to fix itself fast
|
||||
// thus we use an optimistic approach to avoid expensive locks
|
||||
sprintf(client->exepath, "o/%s.XXXXXX.com", basename(exename));
|
||||
int exefd = openatemp(AT_FDCWD, client->exepath, 4, O_CLOEXEC, 0700);
|
||||
ftruncate(exefd, filesize);
|
||||
if (write(exefd, exe, filesize) != filesize) {
|
||||
WARNF("%s failed to write %#s", addrstr, exename);
|
||||
close(exefd);
|
||||
pthread_exit(0);
|
||||
}
|
||||
if (close(exefd)) {
|
||||
WARNF("%s failed to close %#s", addrstr, exename);
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
// do the args
|
||||
int i = 0;
|
||||
char *args[8] = {0};
|
||||
if (!IsXnuSilicon()) {
|
||||
exe = client->exepath;
|
||||
} else {
|
||||
exe = "ape-m1.com";
|
||||
args[i++] = (char *)exe;
|
||||
args[i++] = "-";
|
||||
args[i++] = client->exepath;
|
||||
}
|
||||
args[i++] = client->exepath;
|
||||
if (use_strace) args[i++] = "--strace";
|
||||
if (use_ftrace) args[i++] = "--ftrace";
|
||||
|
||||
// run program, tee'ing stderr to both log and client
|
||||
DEBUF("spawning %s", client->exepath);
|
||||
sigemptyset(&sigmask);
|
||||
sigaddset(&sigmask, SIGINT);
|
||||
sigaddset(&sigmask, SIGQUIT);
|
||||
sigaddset(&sigmask, SIGCHLD);
|
||||
sigprocmask(SIG_BLOCK, &sigmask, 0);
|
||||
CHECK_NE(-1, pipe2(pipefds, O_CLOEXEC));
|
||||
CHECK_NE(-1, (child = fork()));
|
||||
if (!child) {
|
||||
dup2(g_bogusfd, 0);
|
||||
dup2(pipefds[1], 1);
|
||||
dup2(pipefds[1], 2);
|
||||
sigemptyset(&sigmask);
|
||||
sigprocmask(SIG_SETMASK, &sigmask, 0);
|
||||
int i = 0;
|
||||
const char *exe;
|
||||
char *args[8] = {0};
|
||||
if (!IsXnuSilicon()) {
|
||||
exe = g_exepath;
|
||||
} else {
|
||||
exe = "ape-m1.com";
|
||||
args[i++] = (char *)exe;
|
||||
args[i++] = "-";
|
||||
args[i++] = g_exepath;
|
||||
|
||||
// spawn the program
|
||||
int etxtbsy_tries = 0;
|
||||
RetryOnEtxtbsyRaceCondition:
|
||||
if (etxtbsy_tries++) {
|
||||
if (etxtbsy_tries == 24) { // ~30 seconds
|
||||
WARNF("%s failed to spawn on %s due because either (1) the ETXTBSY race "
|
||||
"condition kept happening or (2) the program in question actually "
|
||||
"is crashing with SIGVTALRM, without printing anything to out/err!",
|
||||
exename, g_hostname);
|
||||
pthread_exit(0);
|
||||
}
|
||||
if (usleep(1u << etxtbsy_tries)) {
|
||||
INFOF("interrupted exponential spawn backoff");
|
||||
pthread_exit(0);
|
||||
}
|
||||
args[i++] = g_exepath;
|
||||
if (use_strace) args[i++] = "--strace";
|
||||
if (use_ftrace) args[i++] = "--ftrace";
|
||||
execvp(exe, args);
|
||||
_Exit(127);
|
||||
}
|
||||
signal(SIGINT, SIG_IGN);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
close(pipefds[1]);
|
||||
DEBUGF("communicating %s[%d]", exename, child);
|
||||
deadline =
|
||||
errno_t err;
|
||||
posix_spawnattr_t spawnattr;
|
||||
posix_spawn_file_actions_t spawnfila;
|
||||
sigemptyset(&sigmask);
|
||||
pipe2(client->pipe, O_CLOEXEC);
|
||||
posix_spawnattr_init(&spawnattr);
|
||||
posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_SETPGROUP);
|
||||
posix_spawnattr_setsigmask(&spawnattr, &sigmask);
|
||||
posix_spawn_file_actions_init(&spawnfila);
|
||||
posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);
|
||||
posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 1);
|
||||
posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 2);
|
||||
err = posix_spawn(&client->pid, exe, &spawnfila, &spawnattr, args, environ);
|
||||
if (err) {
|
||||
Close(&client->pipe[1]);
|
||||
Close(&client->pipe[0]);
|
||||
if (err == ETXTBSY) {
|
||||
goto RetryOnEtxtbsyRaceCondition;
|
||||
}
|
||||
WARNF("%s failed to spawn on %s due to %s", exename, g_hostname,
|
||||
strerror(err));
|
||||
pthread_exit(0);
|
||||
}
|
||||
posix_spawn_file_actions_destroy(&spawnfila);
|
||||
posix_spawnattr_destroy(&spawnattr);
|
||||
Close(&client->pipe[1]);
|
||||
|
||||
DEBUF("communicating %s[%d]", exename, client->pid);
|
||||
struct timespec deadline =
|
||||
timespec_add(timespec_real(), timespec_fromseconds(DEATH_CLOCK_SECONDS));
|
||||
for (;;) {
|
||||
now = timespec_real();
|
||||
if (timespec_cmp(now, deadline) >= 0) {
|
||||
WARNF("%s worker timed out", exename);
|
||||
if (g_interrupted) {
|
||||
WARNF("killing %d %s and hanging up %d due to interrupt", client->fd,
|
||||
exename, client->pid);
|
||||
HangupClientAndTerminateJob:
|
||||
SendProgramOutut(client);
|
||||
mbedtls_ssl_close_notify(&ezssl);
|
||||
TerminateJob:
|
||||
LOGIFNEG1(kill(child, 9));
|
||||
LOGIFNEG1(waitpid(child, 0, 0));
|
||||
LOGIFNEG1(close(g_clifd));
|
||||
LOGIFNEG1(close(pipefds[0]));
|
||||
LOGIFNEG1(unlink(g_exepath));
|
||||
_exit(1);
|
||||
PrintProgramOutput(client);
|
||||
pthread_exit(0);
|
||||
}
|
||||
struct timespec now = timespec_real();
|
||||
if (timespec_cmp(now, deadline) >= 0) {
|
||||
WARNF("killing %s (pid %d) which timed out after %d seconds", exename,
|
||||
client->pid, DEATH_CLOCK_SECONDS);
|
||||
goto HangupClientAndTerminateJob;
|
||||
}
|
||||
struct pollfd fds[2];
|
||||
fds[0].fd = g_clifd;
|
||||
fds[0].fd = client->fd;
|
||||
fds[0].events = POLLIN;
|
||||
fds[1].fd = pipefds[0];
|
||||
fds[1].fd = client->pipe[0];
|
||||
fds[1].events = POLLIN;
|
||||
int waitms = timespec_tomillis(timespec_sub(deadline, now));
|
||||
INFOF("polling for %d ms", waitms);
|
||||
events = poll(fds, ARRAYLEN(fds), waitms);
|
||||
CHECK_NE(-1, events); // EINTR shouldn't be possible
|
||||
events = poll(fds, ARRAYLEN(fds),
|
||||
timespec_tomillis(timespec_sub(deadline, now)));
|
||||
if (events == -1) {
|
||||
if (errno == EINTR) {
|
||||
INFOF("poll interrupted");
|
||||
continue;
|
||||
} else {
|
||||
WARNF("killing %d %s and hanging up %d because poll failed", client->fd,
|
||||
exename, client->pid);
|
||||
goto HangupClientAndTerminateJob;
|
||||
}
|
||||
}
|
||||
if (events) {
|
||||
if (fds[0].revents) {
|
||||
int received;
|
||||
char buf[512];
|
||||
INFOF("mbedtls_ssl_read");
|
||||
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
|
||||
if (!received) {
|
||||
WARNF("%s client disconnected so killing worker %d", exename, child);
|
||||
WARNF("%s client disconnected so killing worker %d", exename,
|
||||
client->pid);
|
||||
goto TerminateJob;
|
||||
}
|
||||
if (received > 0) {
|
||||
WARNF("%s client sent %d unexpected bytes so killing job", exename,
|
||||
received);
|
||||
goto TerminateJob;
|
||||
goto HangupClientAndTerminateJob;
|
||||
}
|
||||
if (received != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
WARNF("%s client ssl read failed with -0x%04x so killing job",
|
||||
exename, -received);
|
||||
goto TerminateJob;
|
||||
if (received == MBEDTLS_ERR_SSL_WANT_READ) { // EAGAIN SO_RCVTIMEO
|
||||
WARNF("%s (pid %d) is taking a really long time", exename,
|
||||
client->pid);
|
||||
continue;
|
||||
}
|
||||
INFOF("got spurious ssl data");
|
||||
WARNF("client ssl read failed with -0x%04x (%s) so killing %s",
|
||||
-received, GetTlsError(received), exename);
|
||||
goto TerminateJob;
|
||||
}
|
||||
if (fds[1].revents) {
|
||||
INFOF("read");
|
||||
got = read(pipefds[0], g_buf, sizeof(g_buf));
|
||||
CHECK_NE(-1, got); // EINTR shouldn't be possible
|
||||
char buf[512];
|
||||
ssize_t got = read(client->pipe[0], buf, sizeof(buf));
|
||||
if (got == -1) {
|
||||
WARNF("got %s reading %s output", strerror(errno), exename);
|
||||
goto HangupClientAndTerminateJob;
|
||||
}
|
||||
if (!got) {
|
||||
LOGIFNEG1(close(pipefds[0]));
|
||||
VERBF("got eof reading %s output", exename);
|
||||
Close(&client->pipe[0]);
|
||||
break;
|
||||
}
|
||||
fwrite(g_buf, got, 1, stderr);
|
||||
SendOutputFragmentMessage(kRunitStderr, g_buf, got);
|
||||
DEBUF("got %ld bytes reading %s output", got, exename);
|
||||
appendd(&client->output, buf, got);
|
||||
}
|
||||
}
|
||||
}
|
||||
INFOF("waitpid");
|
||||
CHECK_NE(-1, waitpid(child, &wstatus, 0)); // EINTR shouldn't be possible
|
||||
WaitAgain:
|
||||
DEBUF("waitpid");
|
||||
struct rusage rusage;
|
||||
int wrc = wait4(client->pid, &wstatus, 0, &rusage);
|
||||
if (wrc == -1) {
|
||||
if (errno == EINTR) {
|
||||
WARNF("waitpid interrupted; killing %s pid %d", exename, client->pid);
|
||||
kill(client->pid, SIGINT);
|
||||
goto WaitAgain;
|
||||
}
|
||||
WARNF("waitpid failed %m");
|
||||
client->pid = 0;
|
||||
goto HangupClientAndTerminateJob;
|
||||
}
|
||||
client->pid = 0;
|
||||
int exitcode;
|
||||
if (WIFEXITED(wstatus)) {
|
||||
if (WEXITSTATUS(wstatus)) {
|
||||
WARNF("%s exited with %d", exename, WEXITSTATUS(wstatus));
|
||||
WARNF("%s on %s exited with %d", exename, g_hostname,
|
||||
WEXITSTATUS(wstatus));
|
||||
appendf(&client->output, "------ %s %s $?=%d (0x%08x) ------\n",
|
||||
g_hostname, exename, WEXITSTATUS(wstatus), wstatus);
|
||||
} else {
|
||||
VERBOSEF("%s exited with %d", exename, WEXITSTATUS(wstatus));
|
||||
VERBF("%s on %s exited with %d", exename, g_hostname,
|
||||
WEXITSTATUS(wstatus));
|
||||
}
|
||||
exitcode = WEXITSTATUS(wstatus);
|
||||
} else {
|
||||
WARNF("%s terminated with %s", exename, strsignal(WTERMSIG(wstatus)));
|
||||
} else if (WIFSIGNALED(wstatus)) {
|
||||
if (WTERMSIG(wstatus) == SIGVTALRM && !client->output) {
|
||||
free(client->output);
|
||||
client->output = 0;
|
||||
goto RetryOnEtxtbsyRaceCondition;
|
||||
}
|
||||
WARNF("%s on %s terminated with %s", exename, g_hostname,
|
||||
strsignal(WTERMSIG(wstatus)));
|
||||
exitcode = 128 + WTERMSIG(wstatus);
|
||||
appendf(&client->output, "------ %s %s $?=%s (0x%08x) ------\n", g_hostname,
|
||||
exename, strsignal(WTERMSIG(wstatus)), wstatus);
|
||||
} else {
|
||||
WARNF("%s on %s died with wait status 0x%08x", exename, g_hostname,
|
||||
wstatus);
|
||||
exitcode = 127;
|
||||
}
|
||||
LOGIFNEG1(unlink(g_exepath));
|
||||
if (wstatus) {
|
||||
AppendResourceReport(&client->output, &rusage, "\n");
|
||||
PrintProgramOutput(client);
|
||||
}
|
||||
SendProgramOutut(client);
|
||||
SendExitMessage(exitcode);
|
||||
INFOF("mbedtls_ssl_close_notify");
|
||||
mbedtls_ssl_close_notify(&ezssl);
|
||||
LOGIFNEG1(close(g_clifd));
|
||||
_exit(0);
|
||||
if (etxtbsy_tries) {
|
||||
VERBF("encountered %d ETXTBSY race conditions spawning %s", etxtbsy_tries,
|
||||
exename);
|
||||
}
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
||||
int Poll(void) {
|
||||
int i, wait, evcount;
|
||||
struct pollfd fds[1];
|
||||
TryAgain:
|
||||
if (g_interrupted) return 0;
|
||||
fds[0].fd = g_servfd;
|
||||
fds[0].events = POLLIN | POLLERR | POLLHUP;
|
||||
wait = MIN(1000, g_timeout);
|
||||
evcount = poll(fds, ARRAYLEN(fds), wait);
|
||||
if (!evcount) g_timeout -= wait;
|
||||
if (evcount == -1 && errno == EINTR) goto TryAgain;
|
||||
CHECK_NE(-1, evcount);
|
||||
for (i = 0; i < evcount; ++i) {
|
||||
CHECK(fds[i].revents & POLLIN);
|
||||
HandleClient();
|
||||
void HandleClient(void) {
|
||||
struct Client *client;
|
||||
client = calloc(1, sizeof(struct Client));
|
||||
client->addrsize = sizeof(client->addr);
|
||||
for (;;) {
|
||||
if (g_interrupted) {
|
||||
free(client);
|
||||
return;
|
||||
}
|
||||
// poll() because we use SA_RESTART and accept() is @restartable
|
||||
if (poll(&(struct pollfd){g_servfd, POLLIN}, 1, -1) > 0) {
|
||||
client->fd = accept4(g_servfd, (struct sockaddr *)&client->addr,
|
||||
&client->addrsize, SOCK_CLOEXEC);
|
||||
if (client->fd != -1) {
|
||||
VERBF("accepted client fd %d", client->fd);
|
||||
break;
|
||||
} else if (errno != EINTR && errno != EAGAIN) {
|
||||
WARNF("accept4 failed %m");
|
||||
}
|
||||
} else if (errno != EINTR && errno != EAGAIN) {
|
||||
WARNF("poll failed %m");
|
||||
}
|
||||
}
|
||||
/* manually do this because of nt */
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0) donothing;
|
||||
return evcount;
|
||||
sigset_t mask;
|
||||
pthread_attr_t attr;
|
||||
sigfillset(&mask);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setsigmask_np(&attr, &mask);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&client->th, &attr, ClientWorker, client);
|
||||
pthread_attr_destroy(&attr);
|
||||
}
|
||||
|
||||
int Serve(void) {
|
||||
sigset_t mask;
|
||||
StartTcpServer();
|
||||
sigaction(SIGINT, (&(struct sigaction){.sa_handler = OnInterrupt}), 0);
|
||||
sigaction(SIGCHLD,
|
||||
(&(struct sigaction){.sa_handler = OnChildTerminated,
|
||||
.sa_flags = SA_RESTART}),
|
||||
0);
|
||||
for (;;) {
|
||||
if (!Poll() && (!g_timeout || g_interrupted)) break;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
signal(SIGINT, OnInterrupt);
|
||||
sigprocmask(SIG_BLOCK, &mask, 0);
|
||||
while (!g_interrupted) {
|
||||
HandleClient();
|
||||
}
|
||||
if (g_interrupted) {
|
||||
WARNF("got ctrl-c, shutting down...");
|
||||
}
|
||||
WARNF("server exiting");
|
||||
close(g_servfd);
|
||||
if (!g_timeout) {
|
||||
INFOF("timeout expired, shutting down");
|
||||
} else {
|
||||
INFOF("got ctrl-c, shutting down");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Daemonize(void) {
|
||||
VERBF("Daemonize");
|
||||
struct stat st;
|
||||
if (fork() > 0) _exit(0);
|
||||
setsid();
|
||||
|
@ -579,19 +761,29 @@ void Daemonize(void) {
|
|||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int i;
|
||||
SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, GetRunitPsk());
|
||||
__log_level = kLogWarn;
|
||||
#if IsModeDbg()
|
||||
ShowCrashReports();
|
||||
#endif
|
||||
GetOpts(argc, argv);
|
||||
for (i = 3; i < 16; ++i) close(i);
|
||||
g_psk = GetRunitPsk();
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
setenv("TZ", "PST", true);
|
||||
gethostname(g_hostname, sizeof(g_hostname));
|
||||
for (int i = 3; i < 16; ++i) close(i);
|
||||
errno = 0;
|
||||
// poll()'ing /dev/null stdin file descriptor on xnu returns POLLNVAL?!
|
||||
if (IsWindows()) {
|
||||
CHECK_EQ(3, (g_bogusfd = open("/dev/null", O_RDONLY | O_CLOEXEC)));
|
||||
g_bogusfd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
} else {
|
||||
CHECK_EQ(3, (g_bogusfd = open("/dev/zero", O_RDONLY | O_CLOEXEC)));
|
||||
g_bogusfd = open("/dev/zero", O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
if (!isdirectory("o")) CHECK_NE(-1, mkdir("o", 0700));
|
||||
if (g_daemonize) Daemonize();
|
||||
return Serve();
|
||||
mkdir("o", 0700);
|
||||
Serve();
|
||||
free(g_psk);
|
||||
#if IsModeDbg()
|
||||
void CheckForMemoryLeaks(void);
|
||||
CheckForMemoryLeaks();
|
||||
#endif
|
||||
pthread_exit(0);
|
||||
}
|
||||
|
|
|
@ -7267,7 +7267,7 @@ function unix.tiocgwinsz(fd) end
|
|||
--- This creates a secure temporary file inside `$TMPDIR`. If it isn't
|
||||
--- defined, then `/tmp` is used on UNIX and GetTempPath() is used on
|
||||
--- the New Technology. This resolution of `$TMPDIR` happens once in a
|
||||
--- ctor, which is copied to the `kTmpDir` global.
|
||||
--- ctor, which is copied to the `kTmpPath` global.
|
||||
---
|
||||
--- Once close() is called, the returned file is guaranteed to be
|
||||
--- deleted automatically. On UNIX the file is unlink()'d before this
|
||||
|
|
|
@ -4711,7 +4711,7 @@ UNIX MODULE
|
|||
This creates a secure temporary file inside `$TMPDIR`. If it isn't
|
||||
defined, then `/tmp` is used on UNIX and GetTempPath() is used on
|
||||
the New Technology. This resolution of `$TMPDIR` happens once in a
|
||||
ctor, which is copied to the `kTmpDir` global.
|
||||
ctor, which is copied to the `kTmpPath` global.
|
||||
|
||||
Once close() is called, the returned file is guaranteed to be
|
||||
deleted automatically. On UNIX the file is unlink()'d before this
|
||||
|
|
|
@ -7,7 +7,6 @@ o/$(MODE)/tool: \
|
|||
o/$(MODE)/tool/build \
|
||||
o/$(MODE)/tool/curl \
|
||||
o/$(MODE)/tool/decode \
|
||||
o/$(MODE)/tool/hello \
|
||||
o/$(MODE)/tool/lambda \
|
||||
o/$(MODE)/tool/net \
|
||||
o/$(MODE)/tool/plinko \
|
||||
|
|
Loading…
Reference in a new issue