mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-26 20:40:28 +00:00
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:
parent
5655c9a4e7
commit
8f522cb702
116 changed files with 1194 additions and 1025 deletions
|
@ -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))
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
38
libc/calls/prctl.internal.h
Normal file
38
libc/calls/prctl.internal.h
Normal 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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue