From 3d2cf95af1bb76f536265f72dfe9f06009dca3b5 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 24 Jul 2022 19:40:32 -0700 Subject: [PATCH] Remove malloc() dependency on pledge() / unveil() This change also fixes a bug with gettid() being incorrect after fork(). We now implement the ENOENT behavior for getauxval(). The getuid() etc. system calls are now faster too. Plus issetugid() will work on BSDs. --- examples/greenbean.c | 7 ++ examples/hello.c | 1 + .../issetugid.c => calls/_getauxval.c} | 25 ++++++- libc/calls/_getauxval.internal.h | 15 ++++ libc/calls/calls.h | 1 + libc/calls/calls.mk | 5 ++ libc/calls/getauxval.c | 8 +- libc/calls/getegid.c | 14 ++-- libc/calls/geteuid.c | 14 ++-- libc/calls/getuid.c | 28 ++++--- libc/calls/issetugid.c | 47 ++++++++++++ libc/{mem => calls}/pledge.c | 74 +++++++++++-------- libc/calls/pledge.internal.h | 10 +++ libc/calls/syscall-sysv.internal.h | 1 + libc/{mem => calls}/unveil.c | 49 ++++++------ libc/intrin/{gettid.greg.c => gettid.c} | 4 +- libc/intrin/promises.c | 1 + libc/intrin/promises.internal.h | 4 +- libc/rand/rand64.c | 3 +- libc/runtime/clktck.c | 3 +- libc/runtime/fork.c | 5 ++ libc/runtime/runtime.h | 1 - libc/runtime/stack.h | 6 ++ libc/sysv/calls/issetugid.s | 2 - libc/sysv/calls/sys_issetugid.s | 2 + libc/sysv/describeos.greg.c | 1 + libc/sysv/syscalls.sh | 2 +- test/libc/{mem => calls}/pledge_test.c | 23 ++++++ test/libc/calls/test.mk | 2 + test/libc/{mem => calls}/unveil_test.c | 0 tool/build/lib/syscall.c | 7 ++ tool/decode/dumpvdso.c | 3 +- tool/viz/printvideo.c | 2 +- 33 files changed, 270 insertions(+), 100 deletions(-) rename libc/{runtime/issetugid.c => calls/_getauxval.c} (72%) create mode 100644 libc/calls/_getauxval.internal.h create mode 100644 libc/calls/issetugid.c rename libc/{mem => calls}/pledge.c (97%) create mode 100644 libc/calls/pledge.internal.h rename libc/{mem => calls}/unveil.c (95%) rename libc/intrin/{gettid.greg.c => gettid.c} (97%) delete mode 100644 libc/sysv/calls/issetugid.s create mode 100644 libc/sysv/calls/sys_issetugid.s rename test/libc/{mem => calls}/pledge_test.c (96%) rename test/libc/{mem => calls}/unveil_test.c (100%) diff --git a/examples/greenbean.c b/examples/greenbean.c index b6c1bb0e6..1c7d3a919 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -25,6 +25,7 @@ #include "libc/macros.internal.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/threaded.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/sysconf.h" @@ -299,6 +300,12 @@ int main(int argc, char *argv[]) { exit(1); } + // secure the server + __enable_threads(); + unveil("/dev/null", "rw"); + unveil(0, 0); + pledge("stdio inet", 0); + // spawn over 9,000 worker threads th = calloc(threads, sizeof(*th)); for (i = 0; i < threads; ++i) { diff --git a/examples/hello.c b/examples/hello.c index a9c3249f3..0e42430d0 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -7,6 +7,7 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/calls/calls.h" #include "libc/stdio/stdio.h" STATIC_YOINK("mmap"); // TODO: fix bandaid for MODE=asan diff --git a/libc/runtime/issetugid.c b/libc/calls/_getauxval.c similarity index 72% rename from libc/runtime/issetugid.c rename to libc/calls/_getauxval.c index 61aeb30d9..d1cc56d82 100644 --- a/libc/runtime/issetugid.c +++ b/libc/calls/_getauxval.c @@ -16,7 +16,28 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/_getauxval.internal.h" #include "libc/runtime/runtime.h" -#include "libc/sysv/consts/auxv.h" -int issetugid(void) { return !!getauxval(AT_SECURE); } +/** + * Returns auxiliary value, or zero if kernel didn't provide it. + * + * This function is typically regarded as a libc implementation detail; + * thus, the source code is the documentation. + * + * @param at is `AT_...` search key + * @return true if value was found + * @see libc/sysv/consts.sh + * @see System Five Application Binary Interface § 3.4.3 + * @error ENOENT when value not found + * @asyncsignalsafe + */ +struct AuxiliaryValue _getauxval(unsigned long at) { + unsigned long *ap; + for (ap = __auxv; ap[0]; ap += 2) { + if (at == ap[0]) { + return (struct AuxiliaryValue){ap[1], true}; + } + } + return (struct AuxiliaryValue){0, false}; +} diff --git a/libc/calls/_getauxval.internal.h b/libc/calls/_getauxval.internal.h new file mode 100644 index 000000000..d2a1177ed --- /dev/null +++ b/libc/calls/_getauxval.internal.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS__GETAUXVAL_H_ +#define COSMOPOLITAN_LIBC_CALLS__GETAUXVAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct AuxiliaryValue { + unsigned long value; + bool isfound; +}; + +struct AuxiliaryValue _getauxval(unsigned long); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS__GETAUXVAL_H_ */ diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 1bb55360d..61e0a7d19 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -115,6 +115,7 @@ int gettid(void) libcesque; int getuid(void) libcesque; int ioprio_get(int, int); int ioprio_set(int, int, int); +int issetugid(void); int kill(int, int); int killpg(int, int); int link(const char *, const char *) dontthrow; diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 75b99013c..4de9fca70 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -179,6 +179,11 @@ o//libc/calls/getcwd-xnu.greg.o: \ OVERRIDE_CFLAGS += \ -Os +o/$(MODE)/libc/calls/pledge.o \ +o/$(MODE)/libc/calls/unveil.o: \ + OVERRIDE_CFLAGS += \ + -DSTACK_FRAME_UNLIMITED + LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x))) LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS)) LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/calls/getauxval.c b/libc/calls/getauxval.c index 7b43ffa50..f1fb2e2d4 100644 --- a/libc/calls/getauxval.c +++ b/libc/calls/getauxval.c @@ -16,10 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/dce.h" #include "libc/runtime/runtime.h" -#include "libc/str/str.h" -#include "libc/sysv/consts/auxv.h" +#include "libc/sysv/errfuns.h" /** * Returns auxiliary value, or zero if kernel didn't provide it. @@ -27,9 +25,10 @@ * This function is typically regarded as a libc implementation detail; * thus, the source code is the documentation. * - * @return aux val or 0 if not available + * @return auxiliary value or 0 if `at` not found * @see libc/sysv/consts.sh * @see System Five Application Binary Interface § 3.4.3 + * @error ENOENT when value not found * @asyncsignalsafe */ unsigned long getauxval(unsigned long at) { @@ -39,5 +38,6 @@ unsigned long getauxval(unsigned long at) { return ap[1]; } } + enoent(); return 0; } diff --git a/libc/calls/getegid.c b/libc/calls/getegid.c index 73ec9dc4b..87b29afea 100644 --- a/libc/calls/getegid.c +++ b/libc/calls/getegid.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/_getauxval.internal.h" #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" #include "libc/calls/syscall-sysv.internal.h" @@ -29,12 +30,13 @@ */ int getegid(void) { int rc; - if (!(rc = getauxval(AT_EGID))) { - if (!IsWindows()) { - rc = sys_getegid(); - } else { - rc = getgid(); - } + struct AuxiliaryValue av; + if ((av = _getauxval(AT_EGID)).isfound) { + rc = av.value; + } else if (!IsWindows()) { + rc = sys_getegid(); + } else { + rc = getgid(); } STRACE("%s() → %d% m", "getegid", rc); return rc; diff --git a/libc/calls/geteuid.c b/libc/calls/geteuid.c index 7c5d7ac1f..89f3058c0 100644 --- a/libc/calls/geteuid.c +++ b/libc/calls/geteuid.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/_getauxval.internal.h" #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" #include "libc/calls/syscall-sysv.internal.h" @@ -28,12 +29,13 @@ */ int geteuid(void) { int rc; - if (!(rc = getauxval(AT_EUID))) { - if (!IsWindows()) { - rc = sys_geteuid(); - } else { - rc = getuid(); - } + struct AuxiliaryValue av; + if ((av = _getauxval(AT_EUID)).isfound) { + rc = av.value; + } else if (!IsWindows()) { + rc = sys_geteuid(); + } else { + rc = getuid(); } STRACE("%s() → %d% m", "geteuid", rc); return rc; diff --git a/libc/calls/getuid.c b/libc/calls/getuid.c index f7b73cc06..fec366a7e 100644 --- a/libc/calls/getuid.c +++ b/libc/calls/getuid.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/_getauxval.internal.h" #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" #include "libc/calls/syscall-sysv.internal.h" @@ -53,13 +54,15 @@ static textwindows dontinline uint32_t GetUserNameHash(void) { */ int getuid(void) { int rc; - if (!(rc = getauxval(AT_UID))) { - if (!IsWindows()) { - rc = sys_getuid(); - } else { - rc = GetUserNameHash(); - } + struct AuxiliaryValue av; + if ((av = _getauxval(AT_UID)).isfound) { + rc = av.value; + } else if (!IsWindows()) { + rc = sys_getuid(); + } else { + rc = GetUserNameHash(); } + STRACE("%s() → %d% m", "getuid", rc); return rc; } @@ -75,12 +78,13 @@ int getuid(void) { */ int getgid(void) { int rc; - if (!(rc = getauxval(AT_GID))) { - if (!IsWindows()) { - rc = sys_getgid(); - } else { - rc = GetUserNameHash(); - } + struct AuxiliaryValue av; + if ((av = _getauxval(AT_GID)).isfound) { + rc = av.value; + } else if (!IsWindows()) { + rc = sys_getgid(); + } else { + rc = GetUserNameHash(); } STRACE("%s() → %d% m", "getgid", rc); return rc; diff --git a/libc/calls/issetugid.c b/libc/calls/issetugid.c new file mode 100644 index 000000000..525ce83d4 --- /dev/null +++ b/libc/calls/issetugid.c @@ -0,0 +1,47 @@ +/*-*- 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/calls/_getauxval.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/strace.internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" +#include "libc/sysv/consts/auxv.h" + +/** + * Determines if process is tainted. + * + * This function returns 1 if process was launched as a result of an + * execve() call on a binary that had the setuid or setgid bits set. + * FreeBSD defines tainted as including processes that changed their + * effective user / group ids at some point. + * + * @return always successful, 1 if yes, 0 if no + */ +int issetugid(void) { + int rc; + if (IsLinux()) { + rc = !!_getauxval(AT_SECURE).value; + } else if (IsMetal()) { + rc = 0; + } else { + rc = sys_issetugid(); + } + STRACE("issetugid() → %d", rc); + return rc; +} diff --git a/libc/mem/pledge.c b/libc/calls/pledge.c similarity index 97% rename from libc/mem/pledge.c rename to libc/calls/pledge.c index 156ed8a4d..d7b746a9c 100644 --- a/libc/mem/pledge.c +++ b/libc/calls/pledge.c @@ -23,20 +23,13 @@ #include "libc/calls/struct/filter.h" #include "libc/calls/struct/seccomp.h" #include "libc/calls/syscall-sysv.internal.h" -#include "libc/calls/syscall_support-sysv.internal.h" -#include "libc/dce.h" #include "libc/intrin/promises.internal.h" #include "libc/limits.h" #include "libc/macros.internal.h" -#include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/sock/struct/sockaddr.h" +#include "libc/runtime/stack.h" #include "libc/str/str.h" -#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/audit.h" -#include "libc/sysv/consts/clone.h" -#include "libc/sysv/consts/f.h" -#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/nrlinux.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/pr.h" @@ -71,7 +64,7 @@ struct Filter { size_t n; - struct sock_filter *p; + struct sock_filter p[700]; }; static const uint16_t kPledgeLinuxDefault[] = { @@ -453,14 +446,14 @@ static const struct sock_filter kFilterEnd[] = { }; static bool AppendFilter(struct Filter *f, struct sock_filter *p, size_t n) { - size_t m; - struct sock_filter *q; - m = f->n + n; - if (!(q = realloc(f->p, m * sizeof(*f->p)))) return false; - memcpy(q + f->n, p, n * sizeof(*q)); - f->p = q; - f->n = m; - return true; + if (f->n + n <= ARRAYLEN(f->p)) { + memcpy(f->p + f->n, p, n * sizeof(*f->p)); + f->n += n; + return true; + } else { + enomem(); + return false; + } } // SYSCALL is only allowed in the .privileged section @@ -625,6 +618,7 @@ static bool AllowSetsockoptRestrict(struct Filter *f) { /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 1, 0), /* L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6, 0, 20 - 5), /* L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), + /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0f, 13, 0), /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x03, 12, 0), /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0c, 11, 0), /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x13, 10, 0), @@ -1176,7 +1170,7 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { return false; } } else { - asm("ud2"); // list of ordinals exceeds max displacement + asm("hlt"); // list of ordinals exceeds max displacement unreachable; } } @@ -1264,7 +1258,7 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { if (!AllowPrlimitStdio(f)) return false; break; default: - asm("ud2"); // switch forgot to define a special ordinal + asm("hlt"); // switch forgot to define a special ordinal unreachable; } } @@ -1275,7 +1269,9 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { int sys_pledge_linux(unsigned long ipromises) { bool ok = true; int i, rc = -1; - struct Filter f = {0}; + struct Filter f; + CheckLargeStackAllocation(&f, sizeof(f)); + f.n = 0; ipromises = ~ipromises; if (AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && ((ipromises & (1ul << PROMISE_EXEC)) || @@ -1301,7 +1297,6 @@ int sys_pledge_linux(unsigned long ipromises) { rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox); } } - free(f.p); return rc; } @@ -1320,20 +1315,23 @@ int ParsePromises(const char *promises, unsigned long *out) { int rc = 0; int promise; unsigned long ipromises; - char *tok, *state, *start, *freeme; + char *tok, *state, *start, buf[256]; if (promises) { ipromises = -1; - freeme = start = strdup(promises); - while ((tok = strtok_r(start, " \t\r\n", &state))) { - if ((promise = FindPromise(tok)) != -1) { - ipromises &= ~(1ULL << promise); - } else { - rc = einval(); - break; + if (memccpy(buf, promises, 0, sizeof(buf))) { + start = buf; + while ((tok = strtok_r(start, " \t\r\n", &state))) { + if ((promise = FindPromise(tok)) != -1) { + ipromises &= ~(1ULL << promise); + } else { + rc = einval(); + break; + } + start = 0; } - start = 0; + } else { + rc = einval(); } - free(freeme); } else { ipromises = 0; } @@ -1356,6 +1354,18 @@ int ParsePromises(const char *promises, unsigned long *out) { * OpenBSD just kills the process while logging a helpful message to * /var/log/messages explaining which promise category you needed. * + * Timing is everything with pledge. For example, if you're using + * threads, then you may want to enable them explicitly *before* calling + * pledge(), since otherwise you'd need "prot_exec": + * + * __enable_threads(); + * pledge("...", 0); + * + * If you want crash reports, then you can avoid needing "rpath" with: + * + * ShowCrashReports(); + * pledge("...", 0); + * * By default exit() is allowed. This is useful for processes that * perform pure computation and interface with the parent via shared * memory. On Linux we mean sys_exit (_Exit1), not sys_exit_group @@ -1520,7 +1530,7 @@ int pledge(const char *promises, const char *execpromises) { } else { rc = sys_pledge(promises, execpromises); } - if (!rc) { + if (!rc && (IsOpenbsd() || getpid() == gettid())) { __promises = ipromises; __execpromises = iexecpromises; } diff --git a/libc/calls/pledge.internal.h b/libc/calls/pledge.internal.h new file mode 100644 index 000000000..50bd40b1a --- /dev/null +++ b/libc/calls/pledge.internal.h @@ -0,0 +1,10 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +int ParsePromises(const char *, unsigned long *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_ */ diff --git a/libc/calls/syscall-sysv.internal.h b/libc/calls/syscall-sysv.internal.h index 516346add..e29812a31 100644 --- a/libc/calls/syscall-sysv.internal.h +++ b/libc/calls/syscall-sysv.internal.h @@ -57,6 +57,7 @@ i32 sys_getresuid(u32 *, u32 *, u32 *); i32 sys_getsid(int) hidden; i32 sys_gettid(void) hidden; i32 sys_ioctl(i32, u64, ...) hidden; +i32 sys_issetugid(void) hidden; i32 sys_kill(i32, i32, i32) hidden; i32 sys_linkat(i32, const char *, i32, const char *, i32) hidden; i32 sys_lseek(i32, i64, i64, i64) hidden; diff --git a/libc/mem/unveil.c b/libc/calls/unveil.c similarity index 95% rename from libc/mem/unveil.c rename to libc/calls/unveil.c index ba6a26d8d..5b1327170 100644 --- a/libc/mem/unveil.c +++ b/libc/calls/unveil.c @@ -31,10 +31,10 @@ #include "libc/fmt/conv.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" -#include "libc/mem/mem.h" #include "libc/nexgen32e/threaded.h" #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/stack.h" #include "libc/str/path.h" #include "libc/str/str.h" #include "libc/sysv/consts/at.h" @@ -142,8 +142,19 @@ static int unveil_init(void) { return 0; } -static int sys_unveil_linux(const char *path, const char *permissions) { +int sys_unveil_linux(const char *path, const char *permissions) { int rc; + const char *dir; + const char *last; + const char *next; + struct { + char lbuf[PATH_MAX]; + char buf1[PATH_MAX]; + char buf2[PATH_MAX]; + char buf3[PATH_MAX]; + char buf4[PATH_MAX]; + } b; + CheckLargeStackAllocation(&b, sizeof(b)); if (!State.fd && (rc = unveil_init()) == -1) return rc; if ((path && !permissions) || (!path && permissions)) return einval(); @@ -173,48 +184,34 @@ static int sys_unveil_linux(const char *path, const char *permissions) { // realpath(path) to the ruleset. however a corner case exists where // it isn't valid, e.g. /dev/stdin -> /proc/2834/fd/pipe:[51032], so // we'll need to work around this, by adding the path which is valid - const char *dir; - const char *last; - const char *next; - struct { - char lbuf[PATH_MAX]; - char buf1[PATH_MAX]; - char buf2[PATH_MAX]; - char buf3[PATH_MAX]; - char buf4[PATH_MAX]; - } * b; if (strlen(path) + 1 > PATH_MAX) return enametoolong(); - if (!(b = malloc(sizeof(*b)))) return -1; last = path; next = path; for (int i = 0;; ++i) { if (i == 64) { // give up - free(b); return eloop(); } int err = errno; - if ((rc = sys_readlinkat(AT_FDCWD, next, b->lbuf, PATH_MAX)) != -1) { + if ((rc = sys_readlinkat(AT_FDCWD, next, b.lbuf, PATH_MAX)) != -1) { if (rc < PATH_MAX) { // we need to nul-terminate - b->lbuf[rc] = 0; + b.lbuf[rc] = 0; // last = next - strcpy(b->buf1, next); - last = b->buf1; + strcpy(b.buf1, next); + last = b.buf1; // next = join(dirname(next), link) - strcpy(b->buf2, next); - dir = dirname(b->buf2); - if ((next = _joinpaths(b->buf3, PATH_MAX, dir, b->lbuf))) { + strcpy(b.buf2, next); + dir = dirname(b.buf2); + if ((next = _joinpaths(b.buf3, PATH_MAX, dir, b.lbuf))) { // next now points to either: buf3, buf2, lbuf, rodata - strcpy(b->buf4, next); - next = b->buf4; + strcpy(b.buf4, next); + next = b.buf4; } else { - free(b); return enametoolong(); } } else { // symbolic link data was too long - free(b); return enametoolong(); } } else if (errno == EINVAL) { @@ -229,14 +226,12 @@ static int sys_unveil_linux(const char *path, const char *permissions) { break; } else { // readlink failed for some other reason - free(b); return -1; } } // now we can open the path rc = sys_open(path, O_PATH | O_NOFOLLOW | O_CLOEXEC, 0); - free(b); if (rc == -1) return rc; pb.parent_fd = rc; diff --git a/libc/intrin/gettid.greg.c b/libc/intrin/gettid.c similarity index 97% rename from libc/intrin/gettid.greg.c rename to libc/intrin/gettid.c index f3b08f6e9..77ac11678 100644 --- a/libc/intrin/gettid.greg.c +++ b/libc/intrin/gettid.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/state.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/nexgen32e/gettls.h" @@ -49,10 +50,11 @@ * @return thread id greater than zero or -1 w/ errno * @asyncsignalsafe * @threadsafe + * @vforksafe */ int gettid(void) { int tid; - if (__tls_enabled) { + if (__tls_enabled && !__vforked) { tid = *(int *)(__get_tls() + 0x38); if (tid > 0) { return tid; diff --git a/libc/intrin/promises.c b/libc/intrin/promises.c index 19085b423..210dc8119 100644 --- a/libc/intrin/promises.c +++ b/libc/intrin/promises.c @@ -18,5 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/promises.internal.h" +// XXX: should be inherited thread local unsigned long __promises; unsigned long __execpromises; diff --git a/libc/intrin/promises.internal.h b/libc/intrin/promises.internal.h index 94ca50f29..a7d1d1c49 100644 --- a/libc/intrin/promises.internal.h +++ b/libc/intrin/promises.internal.h @@ -28,8 +28,8 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -hidden extern unsigned long __promises; -hidden extern unsigned long __execpromises; +extern unsigned long __promises; +extern unsigned long __execpromises; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/rand/rand64.c b/libc/rand/rand64.c index bd0dbfd5d..c020c14c5 100644 --- a/libc/rand/rand64.c +++ b/libc/rand/rand64.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/_getauxval.internal.h" #include "libc/intrin/pthread.h" #include "libc/intrin/spinlock.h" #include "libc/nexgen32e/rdtsc.h" @@ -54,7 +55,7 @@ uint64_t rand64(void) { s = g_rand64.thepool; // normal path } else { if (!g_rand64.thepid) { - if (AT_RANDOM && (p = (void *)getauxval(AT_RANDOM))) { + if (AT_RANDOM && (p = (void *)_getauxval(AT_RANDOM).value)) { // linux / freebsd kernel supplied entropy memcpy(&s, p, 16); } else { diff --git a/libc/runtime/clktck.c b/libc/runtime/clktck.c index 3617f430e..db78eee07 100644 --- a/libc/runtime/clktck.c +++ b/libc/runtime/clktck.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/_getauxval.internal.h" #include "libc/calls/calls.h" #include "libc/dce.h" #include "libc/fmt/conv.h" @@ -54,7 +55,7 @@ static dontinline int __clk_tck_init(void) { x = -1; } } else { - x = getauxval(AT_CLKTCK); + x = _getauxval(AT_CLKTCK).value; } if (x < 1) x = 100; clk_tck = x; diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index 252f5a1a3..099fc680f 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -24,6 +24,8 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" +#include "libc/nexgen32e/gettls.h" +#include "libc/nexgen32e/threaded.h" #include "libc/nt/process.h" #include "libc/runtime/internal.h" @@ -56,6 +58,9 @@ int fork(void) { } parent = __pid; __pid = dx; + if (__tls_enabled) { + *(int *)(__get_tls() + 0x38) = IsLinux() ? dx : sys_gettid(); + } STRACE("fork() → 0 (child of %d)", parent); if (weaken(__onfork)) { weaken(__onfork)(); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 72cfc2417..c3a16c559 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -70,7 +70,6 @@ int setenv(const char *, const char *, int) paramsnonnull(); int unsetenv(const char *); int clearenv(void); void fpreset(void); -int issetugid(void); void *mmap(void *, uint64_t, int32_t, int32_t, int32_t, int64_t); void *mremap(void *, size_t, size_t, int, ...); int munmap(void *, uint64_t); diff --git a/libc/runtime/stack.h b/libc/runtime/stack.h index 62783f645..9e438984e 100644 --- a/libc/runtime/stack.h +++ b/libc/runtime/stack.h @@ -128,6 +128,12 @@ extern char ape_stack_align[] __attribute__((__weak__)); (IsTiny() || \ (intptr_t)__builtin_frame_address(0) >= GetStackAddr() + PAGESIZE + (n)) +forceinline void CheckLargeStackAllocation(void *p, ssize_t n) { + for (; n > 0; n -= 4096) { + ((char *)p)[n - 1] = 0; + } +} + COSMOPOLITAN_C_END_ #endif /* GNU ELF */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/sysv/calls/issetugid.s b/libc/sysv/calls/issetugid.s deleted file mode 100644 index df46f9a4d..000000000 --- a/libc/sysv/calls/issetugid.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall issetugid,0xfff0fd0fd2147fff,globl diff --git a/libc/sysv/calls/sys_issetugid.s b/libc/sysv/calls/sys_issetugid.s new file mode 100644 index 000000000..565a5575b --- /dev/null +++ b/libc/sysv/calls/sys_issetugid.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall sys_issetugid,0xfff0fd0fd2147fff,globl,hidden diff --git a/libc/sysv/describeos.greg.c b/libc/sysv/describeos.greg.c index 1b0f37c6a..0cdb3ffe7 100644 --- a/libc/sysv/describeos.greg.c +++ b/libc/sysv/describeos.greg.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" +#include "libc/nexgen32e/vendor.internal.h" const char *__describe_os(void) { if (IsLinux()) { diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 1bd182c30..4fd2c644b 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -422,7 +422,7 @@ scall __bsd_seteuid 0xfff0b70b720b7fff globl hidden # wrapped via setreuid() scall __bsd_setegid 0xfff0b60b620b6fff globl hidden # wrapped via setregid() scall fpathconf 0x0c00c00c020c0fff globl scall fhopen 0x18c10812a20f8fff globl -scall issetugid 0xfff0fd0fd2147fff globl +scall sys_issetugid 0xfff0fd0fd2147fff globl hidden scall minherit 0x1110fa0fa20fafff globl scall pathconf 0x0bf0bf0bf20bffff globl scall sysctl 0x0ca0ca0ca20cafff globl diff --git a/test/libc/mem/pledge_test.c b/test/libc/calls/pledge_test.c similarity index 96% rename from test/libc/mem/pledge_test.c rename to test/libc/calls/pledge_test.c index 16e4167f1..57d9a926a 100644 --- a/test/libc/mem/pledge_test.c +++ b/test/libc/calls/pledge_test.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/ioctl.h" +#include "libc/calls/pledge.internal.h" #include "libc/calls/struct/bpf.h" #include "libc/calls/struct/filter.h" #include "libc/calls/struct/flock.h" @@ -562,6 +563,28 @@ TEST(pledge, threadWithLocks_canCodeMorph) { EXPECT_EQ(0, WEXITSTATUS(ws)); } +TEST(pledge, everything) { + int ws, pid; + if (!fork()) { + // contains 548 bpf instructions [2022-07-24] + ASSERT_SYS(0, 0, + pledge("stdio rpath wpath cpath dpath " + "flock fattr inet unix dns tty " + "recvfd sendfd proc exec id " + "unveil settime prot_exec " + "vminfo tmppath", + "stdio rpath wpath cpath dpath " + "flock fattr inet unix dns tty " + "recvfd sendfd proc exec id " + "unveil settime prot_exec " + "vminfo tmppath")); + _Exit(0); + } + EXPECT_NE(-1, wait(&ws)); + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); +} + TEST(pledge, execWithoutRpath) { int ws, pid; ASSERT_SYS(0, 0, touch("foo", 0644)); diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index 56e81e23c..a7a5804e8 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -63,6 +63,8 @@ o/$(MODE)/test/libc/calls/%.com.dbg: \ o/$(MODE)/test/libc/calls/life-nomod.com.zip.o \ o/$(MODE)/test/libc/calls/life-classic.com.zip.o \ o/$(MODE)/test/libc/calls/tiny64.elf.zip.o \ + o/$(MODE)/test/libc/mem/prog/life.elf.zip.o \ + o/$(MODE)/test/libc/mem/prog/sock.elf.zip.o \ o/$(MODE)/third_party/python/Lib/test/tokenize_tests-latin1-coding-cookie-and-utf8-bom-sig.txt.zip.o \ o/$(MODE)/test/libc/calls/calls.pkg \ $(LIBC_TESTMAIN) \ diff --git a/test/libc/mem/unveil_test.c b/test/libc/calls/unveil_test.c similarity index 100% rename from test/libc/mem/unveil_test.c rename to test/libc/calls/unveil_test.c diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index 225b225c9..c9d1dbb63 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -56,6 +56,7 @@ #include "libc/sysv/consts/msync.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/ok.h" +#include "libc/sysv/consts/pr.h" #include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/rusage.h" #include "libc/sysv/consts/sa.h" @@ -499,6 +500,11 @@ static struct sigaction_linux *CoerceSigactionToLinux( return dst; } +static int OpPrctl(struct Machine *m, int op, int64_t a, int64_t b, int64_t c, + int64_t d) { + return einval(); +} + static int OpArchPrctl(struct Machine *m, int code, int64_t addr) { switch (code) { case ARCH_SET_GS: @@ -1503,6 +1509,7 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x0A0, setrlimit(di, P(si))); SYSCALL(0x084, utime(PNN(di), PNN(si))); SYSCALL(0x0EB, utimes(P(di), P(si))); + SYSCALL(0x09D, OpPrctl(m, di, si, dx, r0, r8)); SYSCALL(0x09E, OpArchPrctl(m, di, si)); SYSCALL(0x0BA, OpGetTid(m)); SYSCALL(0x0CB, sched_setaffinity(di, si, P(dx))); diff --git a/tool/decode/dumpvdso.c b/tool/decode/dumpvdso.c index e0f64fd46..49d88165d 100644 --- a/tool/decode/dumpvdso.c +++ b/tool/decode/dumpvdso.c @@ -23,7 +23,8 @@ noasan int main(int argc, char *argv[]) { int i = 0; - Elf64_Ehdr *ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); + Elf64_Ehdr *ehdr; + ehdr = (Elf64_Ehdr *)getauxval(AT_SYSINFO_EHDR); if (isatty(1)) exit(1); for (;;) { write(1, ((char *)ehdr) + i++, 1); diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index c538b54bd..5196ae841 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -1426,7 +1426,7 @@ static void PickDefaults(void) { } static void RenounceSpecialPrivileges(void) { - if (getauxval(AT_SECURE)) { + if (issetugid()) { setegid(getgid()); seteuid(getuid()); }