mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-05 02:38:31 +00:00
Overhaul process spawning
This commit is contained in:
parent
99dc1281f5
commit
26e254fb4d
96 changed files with 1848 additions and 1541 deletions
|
@ -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,28 +16,16 @@
|
|||
│ 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"
|
||||
#include "libc/paths.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/errfuns.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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
const void *const g_rdrand_init[] initarray = {rdrand_init};
|
|
@ -1,276 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/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);
|
||||
}
|
|
@ -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,81 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/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();
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue