Make improvements

This change progresses our AARCH64 support:

- The AARCH64 build and tests are now passing
- Add 128-bit floating-point support to printf()
- Fix clone() so it initializes cosmo's x28 TLS register
- Fix TLS memory layout issue with aarch64 _Alignas vars
- Revamp microbenchmarking tools so they work on aarch64
- Make some subtle improvements to aarch64 crash reporting
- Make kisdangerous() memory checks more accurate on aarch64
- Remove sys_open() since it's not available on Linux AARCH64

This change makes general improvements to Cosmo and Redbean:

- Introduce GetHostIsa() function in Redbean
- You can now feature check using pledge(0, 0)
- You can now feature check using unveil("",0)
- Refactor some more x86-specific asm comments
- Refactor and write docs for some libm functions
- Make the mmap() API behave more similar to Linux
- Fix WIFSIGNALED() which wrongly returned true for zero
- Rename some obscure cosmo keywords from noFOO to dontFOO
This commit is contained in:
Justine Tunney 2023-06-03 08:12:13 -07:00
parent 5655c9a4e7
commit 8f522cb702
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
116 changed files with 1194 additions and 1025 deletions

View file

@ -50,11 +50,11 @@
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
#define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
#define WCOREDUMP(s) (0x80 & (s))
#define WCOREDUMP(s) (128 & (s))
#define WEXITSTATUS(s) ((0xff00 & (s)) >> 8)
#define WIFCONTINUED(s) ((s) == 0xffff)
#define WIFEXITED(s) (!WTERMSIG(s))
#define WIFSIGNALED(s) ((0xffff & (s)) - 1u < 0xffu)
#define WIFSIGNALED(s) (((signed char)((127 & (s)) + 1) >> 1) > 0)
#define WIFSTOPPED(s) ((255 & (s)) == 127)
#define WSTOPSIG(s) WEXITSTATUS(s)
#define WTERMSIG(s) (127 & (s))

View file

@ -1,59 +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 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
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/blockcancel.internal.h"
#include "libc/calls/cp.internal.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/posixthread.internal.h"
#include "libc/thread/tls.h"
#ifdef MODE_DBG
int begin_cancellation_point(void) {
int state = 0;
struct CosmoTib *tib;
struct PosixThread *pt;
if (__enable_tls) {
tib = __get_tls();
if ((pt = (struct PosixThread *)tib->tib_pthread)) {
state = pt->flags & PT_INCANCEL;
pt->flags |= PT_INCANCEL;
}
}
return state;
}
void end_cancellation_point(int state) {
struct CosmoTib *tib;
struct PosixThread *pt;
if (__enable_tls) {
tib = __get_tls();
if ((pt = (struct PosixThread *)tib->tib_pthread)) {
pt->flags &= ~PT_INCANCEL;
pt->flags |= state;
}
}
}
void report_cancellation_point(void) {
BLOCK_CANCELLATIONS;
_bt("error: need BEGIN/END_CANCELLATION_POINT\n");
ALLOW_CANCELLATIONS;
}
#endif /* MODE_DBG */

View file

@ -18,6 +18,8 @@
*/
#include "libc/calls/struct/timespec.h"
// TODO(jart): DELETE
/**
* Returns seconds since epoch w/ high-precision.
* @param clockid can be CLOCK_{REALTIME,MONOTONIC}, etc.
@ -27,7 +29,7 @@ long double dtime(int clockid) {
struct timespec tv;
clock_gettime(clockid, &tv);
secs = tv.tv_nsec;
secs *= 1 / 1e9;
secs *= 1e-9;
secs += tv.tv_sec;
return secs;
}

View file

@ -49,7 +49,7 @@ static bool IsApeBinary(const char *path) {
bool res = false;
// TODO(jart): Should we block signals too?
BLOCK_CANCELLATIONS;
if ((fd = sys_open(path, O_RDONLY, 0)) != -1) {
if ((fd = sys_openat(AT_FDCWD, path, O_RDONLY, 0)) != -1) {
res = sys_read(fd, buf, 8) == 8 && IsAPEMagic(buf);
sys_close(fd);
}

View file

@ -76,7 +76,7 @@ static void Refresh(void) {
memcpy(&g_now, &now, sizeof(now));
}
long double ConvertTicksToNanos(uint64_t ticks) {
long double ConvertTicksToNanos(double ticks) {
if (!g_now.once) Refresh();
return ticks * g_now.cpn; /* pico scale */
}

View file

@ -32,6 +32,7 @@
#include "libc/intrin/kprintf.h"
#include "libc/log/rop.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/pty.h"
#include "libc/sysv/consts/termios.h"
@ -55,7 +56,7 @@ static int openpty_impl(int *mfd, int *sfd, char *name,
RETURN_ON_ERROR(grantpt(m));
RETURN_ON_ERROR(unlockpt(m));
RETURN_ON_ERROR(_ptsname(m, t.sname, sizeof(t.sname)));
RETURN_ON_ERROR((s = sys_open(t.sname, O_RDWR, 0)));
RETURN_ON_ERROR((s = sys_openat(AT_FDCWD, t.sname, O_RDWR, 0)));
} else {
RETURN_ON_ERROR(sys_ioctl(m, PTMGET, &t));
close(m);

View file

@ -19,6 +19,7 @@
#include "ape/sections.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/prctl.internal.h"
#include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/filter.h"
#include "libc/calls/struct/seccomp.h"
@ -1100,33 +1101,6 @@ static privileged void Log(const char *s, ...) {
va_end(va);
}
static privileged int Prctl(int op, long a, void *b, long c, long d) {
int rc;
#ifdef __x86_64__
asm volatile("mov\t%5,%%r10\n\t"
"mov\t%6,%%r8\n\t"
"syscall"
: "=a"(rc)
: "0"(__NR_linux_prctl), "D"(op), "S"(a), "d"(b), "g"(c), "g"(d)
: "rcx", "r8", "r10", "r11", "memory");
#elif defined(__aarch64__)
register long r0 asm("x0") = (long)op;
register long r1 asm("x1") = (long)a;
register long r2 asm("x2") = (long)b;
register long r3 asm("x3") = (long)c;
register long r4 asm("x4") = (long)d;
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(__NR_linux_prctl), "r"(r0), "r"(r1), "r"(r2), "r"(r3),
"r"(r4)
: "x8", "memory");
rc = res_x0;
#endif
return rc;
}
static privileged int SigAction(int sig, struct sigaction *act,
struct sigaction *old) {
int res;
@ -2353,18 +2327,18 @@ privileged int sys_pledge_linux(unsigned long ipromises, int mode) {
// PR_SET_SECCOMP (Linux 2.6.23+) will refuse to work if
// PR_SET_NO_NEW_PRIVS (Linux 3.5+) wasn't called so we punt the error
// detection to the seccomp system call below.
Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
sys_prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
// register our seccomp filter with the kernel
struct sock_fprog sandbox = {.len = f.n, .filter = f.p};
rc = Prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox, 0, 0);
rc = sys_prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, (long)&sandbox, 0, 0);
// the EINVAL error could mean a lot of things. it could mean the bpf
// code is broken. it could also mean we're running on RHEL5 which
// doesn't have SECCOMP support. since we don't consider lack of
// system support for security to be an error, we distinguish these
// two cases by running a simpler SECCOMP operation.
if (rc == -Einval && Prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == -Einval) {
if (rc == -Einval && sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0) == -Einval) {
rc = 0; // -Enosys
}

View file

@ -18,6 +18,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/pledge.internal.h"
#include "libc/calls/prctl.internal.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
@ -26,6 +27,7 @@
#include "libc/intrin/strace.internal.h"
#include "libc/nexgen32e/vendor.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/pr.h"
#include "libc/sysv/errfuns.h"
/**
@ -42,8 +44,10 @@
* across execve() if permitted). Root access is not required. Support
* is limited to Linux 2.6.23+ (c. RHEL6) and OpenBSD. If your kernel
* isn't supported, then pledge() will return 0 and do nothing rather
* than raising ENOSYS. We don't consider lack of system support to be
* an error, because the specified operations will be permitted.
* than raising ENOSYS. This implementation doesn't consider lack of
* system support to be an error by default. To perform a functionality
* check, use `pledge(0,0)` which is a no-op that'll fail appropriately
* when the necessary system support isn't available for restrictions.
*
* The promises you give pledge() define which system calls are allowed.
* Error messages are logged when sandbox violations occur, but how that
@ -234,6 +238,7 @@
* subprocesses can't inherit the `SIGSYS` handler this installs.
*
* @return 0 on success, or -1 w/ errno
* @raise ENOSYS if `pledge(0, 0)` was used and security is not possible
* @raise EINVAL if `execpromises` on Linux isn't a subset of `promises`
* @raise EINVAL if `promises` allows exec and `execpromises` is null
* @threadsafe
@ -242,8 +247,24 @@
int pledge(const char *promises, const char *execpromises) {
int e, rc;
unsigned long ipromises, iexecpromises;
if (IsGenuineBlink()) {
rc = 0; // blink doesn't support seccomp
if (!promises) {
// OpenBSD says NULL argument means it doesn't change, i.e.
// pledge(0,0) on OpenBSD does nothing. The Cosmopolitan Libc
// implementation defines pledge(0,0) as a no-op feature check.
// Cosmo pledge() is currently implemented to succeed silently if
// the necessary kernel features aren't supported by the host. Apps
// may use pledge(0,0) to perform a support check, to determine if
// pledge() will be able to impose the restrictions it advertises
// within the host environment.
if (execpromises) return einval();
if (IsGenuineBlink()) return enosys();
if (IsOpenbsd()) return sys_pledge(0, 0);
if (!IsLinux()) return enosys();
if (!(rc = sys_prctl(PR_GET_SECCOMP, 0, 0, 0, 0))) return 0;
errno = -rc;
return -1;
} else if (!IsTiny() && IsGenuineBlink()) {
rc = 0; // blink doesn't support seccomp; avoid noisy log warnings
} else if (!ParsePromises(promises, &ipromises) &&
!ParsePromises(execpromises, &iexecpromises)) {
if (IsLinux()) {

View file

@ -22,6 +22,7 @@
#include "libc/errno.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/errfuns.h"
@ -37,9 +38,9 @@ int posix_openpt(int flags) {
if ((flags & O_ACCMODE) != O_RDWR) {
rc = einval();
} else if (IsLinux() || IsXnu() || IsNetbsd()) {
rc = sys_open("/dev/ptmx", flags, 0);
rc = sys_openat(AT_FDCWD, "/dev/ptmx", flags, 0);
} else if (IsOpenbsd()) {
rc = sys_open("/dev/ptm", flags, 0);
rc = sys_openat(AT_FDCWD, "/dev/ptm", flags, 0);
} else if (IsFreebsd()) {
rc = sys_posix_openpt(flags);
if (rc == -1 && errno == ENOSPC) errno = EAGAIN;

View file

@ -17,6 +17,7 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/prctl.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -43,30 +44,11 @@ privileged int prctl(int operation, ...) {
va_end(va);
if (IsLinux()) {
#ifdef __x86_64__
asm volatile("mov\t%5,%%r10\n\t"
"mov\t%6,%%r8\n\t"
"syscall"
: "=a"(rc)
: "0"(157), "D"(operation), "S"(a), "d"(b), "g"(c), "g"(d)
: "rcx", "r8", "r10", "r11", "memory");
if (rc > -4096u) errno = -rc, rc = -1;
#elif defined(__aarch64__)
register long r0 asm("x0") = (long)operation;
register long r1 asm("x1") = (long)a;
register long r2 asm("x2") = (long)b;
register long r3 asm("x3") = (long)c;
register long r4 asm("x4") = (long)d;
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(167), "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4)
: "x8", "memory");
rc = _sysret(res_x0);
#else
#error "arch unsupported"
#endif
rc = sys_prctl(operation, a, b, c, d);
if (rc < 0) {
errno = -rc;
rc = -1;
}
} else {
rc = enosys();
}

View file

@ -0,0 +1,38 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_PRCTL_INTERNAL_H_
#define COSMOPOLITAN_LIBC_CALLS_PRCTL_INTERNAL_H_
#include "libc/dce.h"
#include "libc/sysv/consts/nrlinux.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
forceinline int sys_prctl(int op, long a, long b, long c, long d) {
int rc;
#ifdef __x86_64__
register long r10 asm("r10") = c;
register long r8 asm("r8") = d;
asm volatile("syscall"
: "=a"(rc)
: "0"(__NR_linux_prctl), "D"(op), "S"(a), "d"(b), "r"(r10),
"r"(r8)
: "rcx", "r11", "memory");
#elif defined(__aarch64__)
register long r0 asm("x0") = op;
register long r1 asm("x1") = a;
register long r2 asm("x2") = b;
register long r3 asm("x3") = c;
register long r4 asm("x4") = d;
register long res_x0 asm("x0");
asm volatile("mov\tx8,%1\n\t"
"svc\t0"
: "=r"(res_x0)
: "i"(__NR_linux_prctl), "r"(r0), "r"(r1), "r"(r2), "r"(r3),
"r"(r4)
: "x8", "memory");
rc = res_x0;
#endif
return rc;
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_PRCTL_INTERNAL_H_ */

View file

@ -75,7 +75,6 @@ i32 sys_mknodat(i32, const char *, u32, u64) _Hide;
i32 sys_mprotect(void *, u64, i32) _Hide;
i32 sys_msync(void *, u64, i32) _Hide;
i32 sys_munmap(void *, u64) _Hide;
i32 sys_open(const char *, i32, u32) _Hide;
i32 sys_openat(i32, const char *, i32, u32) _Hide;
i32 sys_pause(void) _Hide;
i32 sys_pipe(i32[hasatleast 2]) _Hide;

View file

@ -27,8 +27,10 @@
#include "libc/calls/struct/stat.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/vendor.internal.h"
@ -99,9 +101,11 @@ static int landlock_abi_version;
static int landlock_abi_errno;
__attribute__((__constructor__)) void init_landlock_version() {
int e = errno;
landlock_abi_version =
landlock_create_ruleset(0, 0, LANDLOCK_CREATE_RULESET_VERSION);
landlock_abi_errno = errno;
errno = e;
}
/**
@ -170,7 +174,7 @@ static int unveil_init(void) {
const struct landlock_ruleset_attr attr = {
.handled_access_fs = State.fs_mask,
};
// [undocumented] landlock_create_ruleset() always returns o_cloexec
// [undocumented] landlock_create_ruleset() always returns O_CLOEXEC
// assert(__sys_fcntl(rc, F_GETFD, 0) == FD_CLOEXEC);
if ((rc = landlock_create_ruleset(&attr, sizeof(attr), 0)) < 0) return -1;
// grant file descriptor a higher number that's less likely to interfere
@ -274,7 +278,7 @@ int sys_unveil_linux(const char *path, const char *permissions) {
// now we can open the path
BLOCK_CANCELLATIONS;
rc = sys_open(path, O_PATH | O_NOFOLLOW | O_CLOEXEC, 0);
rc = sys_openat(AT_FDCWD, path, O_PATH | O_NOFOLLOW | O_CLOEXEC, 0);
ALLOW_CANCELLATIONS;
if (rc == -1) return rc;
@ -308,9 +312,16 @@ int sys_unveil_linux(const char *path, const char *permissions) {
* should become unhidden. When you're finished, you call `unveil(0,0)`
* which commits your policy.
*
* This function requires OpenBSD or Linux 5.13+. We don't consider lack
* of system support to be an ENOSYS error, because the files will still
* become unveiled. Therefore we return 0 in such cases.
* This function requires OpenBSD or Linux 5.13+ (2022+). If the kernel
* support isn't available (or we're in an emulator like Qemu or Blink)
* then zero is returned and nothing happens (instead of raising ENOSYS)
* because the files are still unveiled. Use `unveil("", 0)` to feature
* check the host system, which is defined as a no-op that'll fail if
* the host system doesn't have the necessary features that allow
* unveil() impose bona-fide security restrictions. Otherwise, if
* everything is good, a return value `>=0` is returned, where `0` means
* OpenBSD, and `>=1` means Linux with Landlock LSM, in which case the
* return code shall be the maximum supported Landlock ABI version.
*
* There are some differences between unveil() on Linux versus OpenBSD.
*
@ -338,10 +349,10 @@ int sys_unveil_linux(const char *path, const char *permissions) {
* possible to use opendir() and go fishing for paths which weren't
* previously known.
*
* 5. Use ftruncate() rather than truncate() if you wish for portability to
* Linux kernels versions released before February 2022. One issue
* Landlock hadn't addressed as of ABI version 2 was restrictions over
* truncate() and setxattr() which could permit certain kinds of
* 5. Use ftruncate() rather than truncate() if you wish for portability
* to Linux kernels versions released before February 2022. One issue
* Landlock hadn't addressed as of ABI version 2 was restrictions
* over truncate() and setxattr() which could permit certain kinds of
* modifications to files outside the sandbox. When your policy is
* committed, we install a SECCOMP BPF filter to disable those calls,
* however similar trickery may be possible through other unaddressed
@ -349,8 +360,8 @@ int sys_unveil_linux(const char *path, const char *permissions) {
* unveil() will solve this, since it installs a strong system call
* access policy. Linux 6.2 has improved this situation with Landlock
* ABI v3, which added the ability to control truncation operations -
* this means the SECCOMP BPF filter will only disable
* truncate() on Linux 6.1 or older
* this means the SECCOMP BPF filter will only disable truncate() on
* Linux 6.1 or older.
*
* 6. Set your process-wide policy at startup from the main thread. On
* OpenBSD unveil() will apply process-wide even when called from a
@ -385,10 +396,14 @@ int sys_unveil_linux(const char *path, const char *permissions) {
* - `c` allows `path` to be created and removed, corresponding to
* the pledge promise "cpath".
*
* @return 0 on success, or -1 w/ errno
* @return 0 on success, or -1 w/ errno; note: if `unveil("",0)` is used
* to perform a feature check, then on Linux a value greater than 0
* shall be returned which is the supported Landlock ABI version
* @raise EPERM if unveil() is called after locking
* @raise EINVAL if one argument is set and the other is not
* @raise EINVAL if an invalid character in `permissions` was found
* @raise EPERM if unveil() is called after locking
* @raise ENOSYS if `unveil("",0)` was used and security isn't possible
* @raise EOPNOTSUPP if `unveil("",0)` was used and Landlock LSM is disabled
* @note on Linux this function requires Linux Kernel 5.13+ and version 6.2+
* to properly support truncation operations
* @see [1] https://docs.kernel.org/userspace-api/landlock.html
@ -397,8 +412,24 @@ int sys_unveil_linux(const char *path, const char *permissions) {
int unveil(const char *path, const char *permissions) {
int e, rc;
e = errno;
if (IsGenuineBlink()) {
rc = 0; // blink doesn't support landlock
if (path && !*path) {
// OpenBSD will always fail on both unveil("",0) and unveil("",""),
// since an empty `path` is invalid and `permissions` is mandatory.
// Cosmopolitan Libc uses it as a feature check convention, to test
// if the host environment enables unveil() to impose true security
// restrictions because the default behavior is to silently succeed
// so that programs will err on the side of working if distributed.
if (IsOpenbsd()) return 0;
if (landlock_abi_version != -1) {
_unassert(landlock_abi_version >= 1);
return landlock_abi_version;
} else {
_unassert(landlock_abi_errno);
errno = landlock_abi_errno;
return -1;
}
} else if (!IsTiny() && IsGenuineBlink()) {
rc = 0; // blink doesn't support landlock; avoid noisy log warnings
} else if (IsLinux()) {
rc = sys_unveil_linux(path, permissions);
} else {