mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-12 22:19:10 +00:00
Overhaul process spawning
This commit is contained in:
parent
99dc1281f5
commit
26e254fb4d
96 changed files with 1848 additions and 1541 deletions
|
@ -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];
|
||||
|
|
276
libc/calls/getrandom.c
Normal file
276
libc/calls/getrandom.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/kcpuids.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nexgen32e/vendor.internal.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/nexgen32e/x86info.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/stdio/xorshift.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/auxv.h"
|
||||
#include "libc/sysv/consts/grnd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
__static_yoink("rdrand_init");
|
||||
|
||||
int sys_getentropy(void *, size_t) asm("sys_getrandom");
|
||||
|
||||
static bool have_getrandom;
|
||||
|
||||
static bool GetRandomRdseed(uint64_t *out) {
|
||||
int i;
|
||||
char cf;
|
||||
uint64_t x;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
asm volatile(CFLAG_ASM("rdseed\t%1")
|
||||
: CFLAG_CONSTRAINT(cf), "=r"(x)
|
||||
: /* no inputs */
|
||||
: "cc");
|
||||
if (cf) {
|
||||
*out = x;
|
||||
return true;
|
||||
}
|
||||
asm volatile("pause");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool GetRandomRdrand(uint64_t *out) {
|
||||
int i;
|
||||
char cf;
|
||||
uint64_t x;
|
||||
for (i = 0; i < 10; ++i) {
|
||||
asm volatile(CFLAG_ASM("rdrand\t%1")
|
||||
: CFLAG_CONSTRAINT(cf), "=r"(x)
|
||||
: /* no inputs */
|
||||
: "cc");
|
||||
if (cf) {
|
||||
*out = x;
|
||||
return true;
|
||||
}
|
||||
asm volatile("pause");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ssize_t GetRandomCpu(char *p, size_t n, int f, bool impl(uint64_t *)) {
|
||||
uint64_t x;
|
||||
size_t i, j;
|
||||
for (i = 0; i < n; i += j) {
|
||||
TryAgain:
|
||||
if (!impl(&x)) {
|
||||
if (f || i >= 256) break;
|
||||
goto TryAgain;
|
||||
}
|
||||
for (j = 0; j < 8 && i + j < n; ++j) {
|
||||
p[i + j] = x;
|
||||
x >>= 8;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static ssize_t GetRandomMetal(char *p, size_t n, int f) {
|
||||
if (f & GRND_RANDOM) {
|
||||
if (X86_HAVE(RDSEED)) {
|
||||
return GetRandomCpu(p, n, f, GetRandomRdseed);
|
||||
} else {
|
||||
return enosys();
|
||||
}
|
||||
} else {
|
||||
if (X86_HAVE(RDRND)) {
|
||||
return GetRandomCpu(p, n, f, GetRandomRdrand);
|
||||
} else {
|
||||
return enosys();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GetRandomEntropy(char *p, size_t n) {
|
||||
unassert(n <= 256);
|
||||
if (sys_getentropy(p, n)) notpossible;
|
||||
}
|
||||
|
||||
static void GetRandomArnd(char *p, size_t n) {
|
||||
size_t m;
|
||||
int cmd[2];
|
||||
cmd[0] = 1; // CTL_KERN
|
||||
cmd[1] = IsFreebsd() ? 37 : 81; // KERN_ARND
|
||||
unassert((m = n) <= 256);
|
||||
if (sys_sysctl(cmd, 2, p, &n, 0, 0) == -1) notpossible;
|
||||
if (m != n) notpossible;
|
||||
}
|
||||
|
||||
static ssize_t GetRandomBsd(char *p, size_t n, void impl(char *, size_t)) {
|
||||
errno_t e;
|
||||
size_t m, i;
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(e = _weaken(pthread_testcancel_np)())) {
|
||||
errno = e;
|
||||
return -1;
|
||||
}
|
||||
for (i = 0;;) {
|
||||
m = MIN(n - i, 256);
|
||||
impl(p + i, m);
|
||||
if ((i += m) == n) {
|
||||
return n;
|
||||
}
|
||||
if (_weaken(pthread_testcancel)) {
|
||||
_weaken(pthread_testcancel)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t GetDevUrandom(char *p, size_t n) {
|
||||
int fd;
|
||||
ssize_t rc;
|
||||
fd = sys_openat(AT_FDCWD, "/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
|
||||
if (fd == -1) return -1;
|
||||
pthread_cleanup_push((void *)sys_close, (void *)(intptr_t)fd);
|
||||
rc = sys_read(fd, p, n);
|
||||
pthread_cleanup_pop(1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
ssize_t __getrandom(void *p, size_t n, unsigned f) {
|
||||
ssize_t rc;
|
||||
if (IsWindows()) {
|
||||
rc = RtlGenRandom(p, n) ? n : __winerr();
|
||||
} else if (have_getrandom) {
|
||||
if (IsXnu() || IsOpenbsd()) {
|
||||
rc = GetRandomBsd(p, n, GetRandomEntropy);
|
||||
} else {
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
rc = sys_getrandom(p, n, f);
|
||||
END_CANCELLATION_POINT;
|
||||
}
|
||||
} else if (IsFreebsd() || IsNetbsd()) {
|
||||
rc = GetRandomBsd(p, n, GetRandomArnd);
|
||||
} else if (IsMetal()) {
|
||||
rc = GetRandomMetal(p, n, f);
|
||||
} else {
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
rc = GetDevUrandom(p, n);
|
||||
END_CANCELLATION_POINT;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns cryptographic random data.
|
||||
*
|
||||
* This random number seed generator obtains information from:
|
||||
*
|
||||
* - RtlGenRandom() on Windows
|
||||
* - getentropy() on XNU and OpenBSD
|
||||
* - getrandom() on Linux, FreeBSD, and NetBSD
|
||||
* - sysctl(KERN_ARND) on older versions of FreeBSD and NetBSD
|
||||
*
|
||||
* Unlike getentropy() this function is interruptible. However EINTR
|
||||
* shouldn't be possible if `f` is zero and `n` is no more than 256,
|
||||
* noting that kernels are a bit vague with their promises here, and
|
||||
* if you're willing to trade some performance for a more assurances
|
||||
* that EINTR won't happen, then either consider using getentropy(),
|
||||
* or using the `SA_RESTART` flag on your signal handlers.
|
||||
*
|
||||
* Unlike getentropy() you may specify an `n` greater than 256. When
|
||||
* larger amounts are specified, the caller must be prepared for the
|
||||
* case where fewer than `n` bytes are returned. In that case, it is
|
||||
* likely that a signal delivery occured. Cancellations in mask mode
|
||||
* also need to be suppressed while processing the bytes beyond 256.
|
||||
* On BSD OSes, this entire process is uninterruptible so be careful
|
||||
* when using large sizes if interruptibility is needed.
|
||||
*
|
||||
* Unlike getentropy() this function is a cancellation point. But it
|
||||
* shouldn't be a problem, unless you're using masked mode, in which
|
||||
* case extra care must be taken to consider the result.
|
||||
*
|
||||
* It's recommended that `f` be set to zero, although it may include
|
||||
* the following flags:
|
||||
*
|
||||
* - `GRND_NONBLOCK` when you want to elevate the insecurity of your
|
||||
* random data
|
||||
*
|
||||
* - `GRND_RANDOM` if you want to have the best possible chance your
|
||||
* program will freeze and the system operator is paged to address
|
||||
* the outage by driving to the data center and jiggling the mouse
|
||||
*
|
||||
* @note this function could block a nontrivial time on old computers
|
||||
* @note this function is indeed intended for cryptography
|
||||
* @note this function takes around 900 cycles
|
||||
* @raise EINVAL if `f` is invalid
|
||||
* @raise ECANCELED if thread was cancelled in masked mode
|
||||
* @raise EFAULT if the `n` bytes at `p` aren't valid memory
|
||||
* @raise EINTR if we needed to block and a signal was delivered instead
|
||||
* @cancellationpoint
|
||||
* @asyncsignalsafe
|
||||
* @restartable
|
||||
* @vforksafe
|
||||
*/
|
||||
ssize_t getrandom(void *p, size_t n, unsigned f) {
|
||||
ssize_t rc;
|
||||
if ((!p && n) || (IsAsan() && !__asan_is_valid(p, n))) {
|
||||
rc = efault();
|
||||
} else if ((f & ~(GRND_RANDOM | GRND_NONBLOCK))) {
|
||||
rc = einval();
|
||||
} else {
|
||||
rc = __getrandom(p, n, f);
|
||||
}
|
||||
STRACE("getrandom(%p, %'zu, %#x) → %'ld% m", p, n, f, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static textstartup void getrandom_init(void) {
|
||||
int e, rc;
|
||||
if (IsWindows() || IsMetal()) return;
|
||||
BLOCK_CANCELLATIONS;
|
||||
e = errno;
|
||||
if (!(rc = sys_getrandom(0, 0, 0))) {
|
||||
have_getrandom = true;
|
||||
} else {
|
||||
errno = e;
|
||||
}
|
||||
ALLOW_CANCELLATIONS;
|
||||
STRACE("sys_getrandom(0,0,0) → %d% m", rc);
|
||||
}
|
|
@ -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);
|
||||
|
|
81
libc/calls/rdrand.c
Normal file
81
libc/calls/rdrand.c
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asmflag.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
#include "libc/sysv/consts/grnd.h"
|
||||
|
||||
__static_yoink("rdrand_init");
|
||||
|
||||
static dontinline uint64_t rdrand_failover(void) {
|
||||
int f;
|
||||
size_t i;
|
||||
ssize_t r;
|
||||
volatile uint64_t b;
|
||||
register uint64_t x;
|
||||
for (f = GRND_RANDOM | GRND_NONBLOCK, i = 0; i < 8; i += r) {
|
||||
if ((r = getrandom((char *)&b + i, 8 - i, f)) <= 0) {
|
||||
if (r == -1 && errno == EINTR) {
|
||||
r = 0;
|
||||
} else if (r == -1 && errno == EAGAIN) {
|
||||
r = 0;
|
||||
f = 0;
|
||||
} else {
|
||||
return _rand64();
|
||||
}
|
||||
}
|
||||
}
|
||||
x = b;
|
||||
b = 0;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves 64-bits of hardware random data from RDRAND instruction.
|
||||
*
|
||||
* If RDRAND isn't available (we check CPUID and we also disable it
|
||||
* automatically for microarchitectures where it's slow or buggy) then
|
||||
* we try getrandom(), RtlGenRandom(), or sysctl(KERN_ARND). If those
|
||||
* aren't available then we try /dev/urandom and if that fails, we try
|
||||
* getauxval(AT_RANDOM), and if not we finally use RDTSC and getpid().
|
||||
*
|
||||
* @note this function could block a nontrivial time on old computers
|
||||
* @note this function is indeed intended for cryptography
|
||||
* @note this function takes around 300 cycles
|
||||
* @see rngset(), rdseed(), _rand64()
|
||||
* @asyncsignalsafe
|
||||
* @vforksafe
|
||||
*/
|
||||
uint64_t rdrand(void) {
|
||||
int i;
|
||||
char cf;
|
||||
uint64_t x;
|
||||
if (X86_HAVE(RDRND)) {
|
||||
for (i = 0; i < 10; ++i) {
|
||||
asm volatile(CFLAG_ASM("rdrand\t%1")
|
||||
: CFLAG_CONSTRAINT(cf), "=r"(x)
|
||||
: /* no inputs */
|
||||
: "cc");
|
||||
if (cf) return x;
|
||||
asm volatile("pause");
|
||||
}
|
||||
}
|
||||
return rdrand_failover();
|
||||
}
|
43
libc/calls/rdrand_init.c
Normal file
43
libc/calls/rdrand_init.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/nexgen32e/kcpuids.h"
|
||||
#include "libc/nexgen32e/vendor.internal.h"
|
||||
#include "libc/nexgen32e/x86feature.h"
|
||||
#include "libc/nexgen32e/x86info.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
|
||||
textstartup void rdrand_init(int argc, char **argv, char **envp,
|
||||
intptr_t *auxv) {
|
||||
extern unsigned kMutableCpuids[KCPUIDS_LEN][4] asm("kCpuids");
|
||||
/*
|
||||
* Clear RDRAND on AMD models before Zen and then some
|
||||
* since it's not only slow but can freeze after sleep
|
||||
* https://bugzilla.redhat.com/show_bug.cgi?id=1150286
|
||||
*/
|
||||
if ((X86_HAVE(RDRND) || X86_HAVE(RDSEED)) &&
|
||||
(IsAuthenticAMD() &&
|
||||
(kX86CpuFamily < 0x17 ||
|
||||
(kX86CpuFamily == 0x17 &&
|
||||
(0x70 <= kX86CpuModel && kX86CpuModel <= 0x7F))))) {
|
||||
kMutableCpuids[KCPUIDS_1H][KCPUIDS_ECX] &= ~(1u << 30);
|
||||
kMutableCpuids[KCPUIDS_7H][KCPUIDS_EBX] &= ~(1u << 18);
|
||||
}
|
||||
}
|
||||
|
||||
const void *const g_rdrand_init[] initarray = {rdrand_init};
|
|
@ -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);
|
||||
|
|
52
libc/calls/tcsetsid.c
Normal file
52
libc/calls/tcsetsid.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*-*- 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 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 │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/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"
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 {
|
||||
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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue