Overhaul process spawning

This commit is contained in:
Justine Tunney 2023-09-10 08:12:43 -07:00
parent 99dc1281f5
commit 26e254fb4d
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
96 changed files with 1848 additions and 1541 deletions

276
libc/calls/getrandom.c Normal file
View 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);
}