From 2e3958c6dc2119ca8ae2fabeaeae63ccda3312d1 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Fri, 15 Jul 2022 18:07:34 -0700 Subject: [PATCH] Make some touchups --- libc/calls/sched_setscheduler.c | 18 +++++-- libc/intrin/promises.c | 21 ++++++++ libc/intrin/promises.internal.h | 33 +++++++++++++ libc/intrin/restoretty.c | 6 +-- libc/log/checkfail.c | 12 +++-- libc/mem/pledge.c | 86 +++++++++++++++++++++------------ libc/runtime/stackuse.c | 6 +-- libc/thread/thread.h | 2 +- test/libc/calls/open_test.c | 9 ++++ test/libc/calls/vfork_test.c | 3 -- third_party/lua/lunix.c | 28 +++++------ 11 files changed, 161 insertions(+), 63 deletions(-) create mode 100644 libc/intrin/promises.c create mode 100644 libc/intrin/promises.internal.h diff --git a/libc/calls/sched_setscheduler.c b/libc/calls/sched_setscheduler.c index 018c29059..a799be801 100644 --- a/libc/calls/sched_setscheduler.c +++ b/libc/calls/sched_setscheduler.c @@ -47,12 +47,22 @@ * The `policy` must have one of: * * - `SCHED_OTHER` (or `SCHED_NORMAL`) for the default policy + * * - `SCHED_RR` for real-time round-robin scheduling + * * - `SCHED_FIFO` for real-time first-in first-out scheduling + * * - `SCHED_BATCH` for "batch" style execution of processes if * supported (Linux), otherwise it's treated as `SCHED_OTHER` - * - `SCHED_IDLE` for running very low priority background jobs - * if it's supported (Linux), otherwise this is `SCHED_OTHER` + * + * - `SCHED_IDLE` for running very low priority background jobs if + * it's supported (Linux), otherwise this is `SCHED_OTHER`. + * Pledging away scheduling privilege is permanent for your + * process; if a subsequent attempt is made to restore the + * `SCHED_OTHER` policy then this system call will `EPERM` (but on + * older kernels like RHEL7 this isn't the case). This policy + * isn't available on old Linux kernels like RHEL5, where it'll + * raise `EINVAL`. * * The `policy` may optionally bitwise-or any one of: * @@ -65,7 +75,9 @@ * greater than or equal to sched_get_priority_min(policy) and less * than or equal to sched_get_priority_max(policy). Linux allows the * static priority range 1 to 99 for the `SCHED_FIFO` and `SCHED_RR` - * policies, and the priority 0 for the remaining policies. + * policies, and the priority 0 is used for the remaining policies. + * You should still consider calling the function, because on NetBSD + * the correct priority might be -1. * * @return the former scheduling policy of the specified process. If * this function fails, then the scheduling policy is not changed, diff --git a/libc/intrin/promises.c b/libc/intrin/promises.c new file mode 100644 index 000000000..78519ed5d --- /dev/null +++ b/libc/intrin/promises.c @@ -0,0 +1,21 @@ +/*-*- 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/intrin/promises.internal.h" + +unsigned long __promises; diff --git a/libc/intrin/promises.internal.h b/libc/intrin/promises.internal.h new file mode 100644 index 000000000..84938cbd3 --- /dev/null +++ b/libc/intrin/promises.internal.h @@ -0,0 +1,33 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_ +#define COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_ + +#define PROMISE_DEFAULT 0 +#define PROMISE_STDIO 1 +#define PROMISE_RPATH 2 +#define PROMISE_WPATH 3 +#define PROMISE_CPATH 4 +#define PROMISE_DPATH 5 +#define PROMISE_FLOCK 6 +#define PROMISE_FATTR 7 +#define PROMISE_INET 8 +#define PROMISE_UNIX 9 +#define PROMISE_DNS 10 +#define PROMISE_TTY 11 +#define PROMISE_RECVFD 12 +#define PROMISE_PROC 13 +#define PROMISE_THREAD 14 +#define PROMISE_EXEC 15 +#define PROMISE_EXECNATIVE 16 +#define PROMISE_ID 17 +#define PROMISE_MAX 17 + +#define PLEDGED(x) (~__promises & (1L << PROMISE_##x)) + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +hidden extern unsigned long __promises; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN_PROMISES_H_ */ diff --git a/libc/intrin/restoretty.c b/libc/intrin/restoretty.c index ee9e63c2f..26d6668ba 100644 --- a/libc/intrin/restoretty.c +++ b/libc/intrin/restoretty.c @@ -23,6 +23,7 @@ #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/promises.internal.h" #include "libc/log/color.internal.h" #include "libc/log/internal.h" #include "libc/log/libfatal.internal.h" @@ -47,8 +48,7 @@ static union metatermios __oldtermios; static textstartup void __oldtermios_init() { int e; e = errno; - if (!IsOpenbsd() && // avoid pledge(tty) - sys_ioctl(0, TCGETS, &__oldtermios) != -1) { + if (PLEDGED(TTY) && sys_ioctl(0, TCGETS, &__oldtermios) != -1) { __isrestorable = true; } errno = e; @@ -60,7 +60,7 @@ const void *const __oldtermios_ctor[] initarray = { void __restore_tty(void) { int e; - if (__isrestorable && !__isworker && !__nocolor) { + if (__isrestorable && PLEDGED(TTY) && !__isworker && !__nocolor) { e = errno; sys_write(0, ANSI_RESTORE, __strlen(ANSI_RESTORE)); sys_ioctl(0, TCSETSF, &__oldtermios); diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index b6efa27a1..5c3295cc8 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -51,10 +51,14 @@ relegated void __check_fail(const char *suffix, const char *opstr, __start_fatal(file, line); __stpcpy(hostname, "unknown"); gethostname(hostname, sizeof(hostname)); - kprintf("check failed on %s pid %d\n", hostname, getpid()); - kprintf("\tCHECK_%^s(%s, %s);\n", suffix, wantstr, gotstr); - kprintf("\t\t → %p (%s)\n", want, wantstr); - kprintf("\t\t%s %p (%s)\n", opstr, got, gotstr); + kprintf("check failed on %s pid %d\n" + "\tCHECK_%^s(%s, %s);\n" + "\t\t → %p (%s)\n" + "\t\t%s %p (%s)\n", // + hostname, getpid(), // + suffix, wantstr, gotstr, // + want, wantstr, // + opstr, got, gotstr); if (!isempty(fmt)) { kprintf("\t"); va_start(va, fmt); diff --git a/libc/mem/pledge.c b/libc/mem/pledge.c index c81dc30c2..d0ecddb11 100644 --- a/libc/mem/pledge.c +++ b/libc/mem/pledge.c @@ -26,6 +26,7 @@ #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/promises.internal.h" #include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/mem/mem.h" @@ -296,7 +297,7 @@ static const uint16_t kPledgeLinuxExec[] = { __NR_linux_openat | READONLY, // }; -static const uint16_t kPledgeLinuxExecnative[] = { +static const uint16_t kPledgeLinuxExec2[] = { __NR_linux_execve, // __NR_linux_execveat, // }; @@ -306,25 +307,25 @@ static const struct Pledges { const uint16_t *syscalls; const size_t len; } kPledgeLinux[] = { - {"default", PLEDGE(kPledgeLinuxDefault)}, // - {"stdio", PLEDGE(kPledgeLinuxStdio)}, // - {"rpath", PLEDGE(kPledgeLinuxRpath)}, // - {"wpath", PLEDGE(kPledgeLinuxWpath)}, // - {"cpath", PLEDGE(kPledgeLinuxCpath)}, // - {"dpath", PLEDGE(kPledgeLinuxDpath)}, // - {"flock", PLEDGE(kPledgeLinuxFlock)}, // - {"fattr", PLEDGE(kPledgeLinuxFattr)}, // - {"inet", PLEDGE(kPledgeLinuxInet)}, // - {"unix", PLEDGE(kPledgeLinuxUnix)}, // - {"dns", PLEDGE(kPledgeLinuxDns)}, // - {"tty", PLEDGE(kPledgeLinuxTty)}, // - {"recvfd", PLEDGE(kPledgeLinuxRecvfd)}, // - {"proc", PLEDGE(kPledgeLinuxProc)}, // - {"thread", PLEDGE(kPledgeLinuxThread)}, // - {"exec", PLEDGE(kPledgeLinuxExec)}, // - {"execnative", PLEDGE(kPledgeLinuxExecnative)}, // - {"id", PLEDGE(kPledgeLinuxId)}, // - {0}, // + [PROMISE_DEFAULT] = {"default", PLEDGE(kPledgeLinuxDefault)}, // + [PROMISE_STDIO] = {"stdio", PLEDGE(kPledgeLinuxStdio)}, // + [PROMISE_RPATH] = {"rpath", PLEDGE(kPledgeLinuxRpath)}, // + [PROMISE_WPATH] = {"wpath", PLEDGE(kPledgeLinuxWpath)}, // + [PROMISE_CPATH] = {"cpath", PLEDGE(kPledgeLinuxCpath)}, // + [PROMISE_DPATH] = {"dpath", PLEDGE(kPledgeLinuxDpath)}, // + [PROMISE_FLOCK] = {"flock", PLEDGE(kPledgeLinuxFlock)}, // + [PROMISE_FATTR] = {"fattr", PLEDGE(kPledgeLinuxFattr)}, // + [PROMISE_INET] = {"inet", PLEDGE(kPledgeLinuxInet)}, // + [PROMISE_UNIX] = {"unix", PLEDGE(kPledgeLinuxUnix)}, // + [PROMISE_DNS] = {"dns", PLEDGE(kPledgeLinuxDns)}, // + [PROMISE_TTY] = {"tty", PLEDGE(kPledgeLinuxTty)}, // + [PROMISE_RECVFD] = {"recvfd", PLEDGE(kPledgeLinuxRecvfd)}, // + [PROMISE_PROC] = {"proc", PLEDGE(kPledgeLinuxProc)}, // + [PROMISE_THREAD] = {"thread", PLEDGE(kPledgeLinuxThread)}, // + [PROMISE_EXEC] = {"exec", PLEDGE(kPledgeLinuxExec)}, // + [PROMISE_EXECNATIVE] = {"execnative", PLEDGE(kPledgeLinuxExec2)}, // + [PROMISE_ID] = {"id", PLEDGE(kPledgeLinuxId)}, // + [PROMISE_MAX + 1] = {0}, // }; static const struct sock_filter kFilterStart[] = { @@ -949,8 +950,8 @@ static bool AllowFchmodat(struct Filter *f) { return AppendFilter(f, PLEDGE(fragment)); } -static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len, - bool needmapexec, bool needmorphing) { +static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len, bool needmapexec, + bool needmorphing) { int i; for (i = 0; i < len; ++i) { switch (p[i]) { @@ -1037,39 +1038,42 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len, return true; } -static const uint16_t *FindPledge(const struct Pledges *p, const char *name, - size_t *len) { +static int FindPromise(const struct Pledges *p, const char *name, size_t *len) { int i; for (i = 0; p[i].name; ++i) { if (!strcasecmp(name, p[i].name)) { *len = p[i].len; - return p[i].syscalls; + return i; } } - return 0; + return -1; } static int sys_pledge_linux(const char *promises, const char *execpromises) { bool ok; int rc = -1; size_t plen; + int promise; bool needmapexec; bool needexecnative; bool needmorphing; struct Filter f = {0}; const uint16_t *pledge; + unsigned long ipromises = -1; char *s, *tok, *state, *start; if (execpromises) return einval(); needmapexec = strstr(promises, "exec"); needmorphing = strstr(promises, "thread"); needexecnative = strstr(promises, "execnative"); - if ((start = s = strdup(promises)) && - AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && + if ((start = s = strdup(promises)) && AppendFilter(&f, kFilterStart, ARRAYLEN(kFilterStart)) && (needmapexec || needexecnative || AppendOriginVerification(&f)) && - AppendPledge(&f, kPledgeLinuxDefault, ARRAYLEN(kPledgeLinuxDefault), - needmapexec, needmorphing)) { + AppendPledge(&f, kPledgeLinuxDefault, ARRAYLEN(kPledgeLinuxDefault), needmapexec, + needmorphing)) { for (ok = true; (tok = strtok_r(start, " \t\r\n", &state)); start = 0) { - if (!(pledge = FindPledge(kPledgeLinux, tok, &plen))) { + if ((promise = FindPromise(kPledgeLinux, tok, &plen)) != -1) { + pledge = kPledgeLinux[promise].syscalls; + ipromises &= ~(1ULL << promise); + } else { ok = false; rc = einval(); break; @@ -1084,12 +1088,29 @@ static int sys_pledge_linux(const char *promises, const char *execpromises) { struct sock_fprog sandbox = {.len = f.n, .filter = f.p}; rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox); } + if (!rc) { + __promises = ipromises; + } } free(f.p); free(s); return rc; } +static void SetPromises(const char *promises) { + int promise; + size_t plen; + char *tok, *state, *start; + unsigned long ipromises = -1; + while ((tok = strtok_r(start, " \t\r\n", &state))) { + if ((promise = FindPromise(kPledgeLinux, tok, &plen)) != -1) { + ipromises &= ~(1ULL << promise); + } + start = 0; + } + __promises = ipromises; +} + /** * Restricts system operations, e.g. * @@ -1216,6 +1237,9 @@ int pledge(const char *promises, const char *execpromises) { rc = sys_pledge_linux(promises, execpromises); } else { rc = sys_pledge(promises, execpromises); + if (!rc) { + SetPromises(promises); + } } STRACE("pledge(%#s, %#s) → %d% m", promises, execpromises, rc); return rc; diff --git a/libc/runtime/stackuse.c b/libc/runtime/stackuse.c index c89f67380..2727bd471 100644 --- a/libc/runtime/stackuse.c +++ b/libc/runtime/stackuse.c @@ -21,6 +21,7 @@ #include "libc/dce.h" #include "libc/fmt/itoa.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/promises.internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/str/str.h" @@ -74,11 +75,8 @@ static textexit void LogStackUse(void) { } static textstartup void LogStackUseInit(void) { - if (IsOpenbsd()) { - // avoid pledge() dependency on wpath - return; - } if (IsTiny()) return; + if (!PLEDGED(WPATH)) return; if (isdirectory("o/" MODE) && getcwd(stacklog, sizeof(stacklog) - strlen("/o/" MODE "/stack.log"))) { strcat(stacklog, "/o/" MODE "/stack.log"); diff --git a/libc/thread/thread.h b/libc/thread/thread.h index 1007c93ee..b17d9c415 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -27,7 +27,7 @@ struct FtraceTls { /* 16 */ struct cthread_descriptor_t { struct cthread_descriptor_t *self; /* 0x00 */ struct FtraceTls ftrace; /* 0x08 */ - int64_t __pad0; /* 0x10 */ + void *garbages; /* 0x10 */ int64_t __pad1; /* 0x20 */ int64_t __pad2; /* 0x28 */ struct cthread_descriptor_t *self2; /* 0x30 */ diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index 3693447eb..ef8dcab07 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -117,6 +117,15 @@ TEST(open, testOpenExistingForAppendWriteOnly_seeksToEnd) { EXPECT_SYS(0, 0, close(3)); } +TEST(open, testRelativePath_opensRelativeToDirFd) { + ASSERT_SYS(0, 0, mkdir("foo", 0755)); + ASSERT_SYS(0, 3, open("foo", O_RDONLY | O_DIRECTORY)); + EXPECT_SYS(0, 4, openat(3, "bar", O_WRONLY | O_TRUNC | O_CREAT, 0755)); + EXPECT_TRUE(fileexists("foo/bar")); + EXPECT_SYS(0, 0, close(4)); + EXPECT_SYS(0, 0, close(3)); +} + int CountFds(void) { int i, count; for (count = i = 0; i < g_fds.n; ++i) { diff --git a/test/libc/calls/vfork_test.c b/test/libc/calls/vfork_test.c index 11444ba68..e2ecd9d1f 100644 --- a/test/libc/calls/vfork_test.c +++ b/test/libc/calls/vfork_test.c @@ -34,9 +34,6 @@ TEST(vfork, test) { ASSERT_NE(-1, lseek(fd, 0, SEEK_SET)); if (!vfork()) { EXPECT_EQ(5, pread(fd, buf, 5, 0)); - /* - * TODO(jart): DOES PREAD IN CHILD REALLY CHANGE PARENT HANDLE POSITION? - */ ASSERT_NE(-1, lseek(fd, 0, SEEK_SET)); EXPECT_STREQ("hello", buf); EXPECT_NE(-1, close(fd)); diff --git a/third_party/lua/lunix.c b/third_party/lua/lunix.c index 3ee02e69e..ba0740dcd 100644 --- a/third_party/lua/lunix.c +++ b/third_party/lua/lunix.c @@ -2585,20 +2585,20 @@ static const luaL_Reg kLuaUnix[] = { {"rmrf", LuaUnixRmrf}, // remove file recursively {"send", LuaUnixSend}, // send tcp to some address {"sendto", LuaUnixSendto}, // send udp to some address - {"setfsgid", LuaUnixSetfsgid}, // set/get group id for file system ops - {"setfsuid", LuaUnixSetfsuid}, // set/get user id for file system ops - {"setgid", LuaUnixSetgid}, // set real group id of process - {"setitimer", LuaUnixSetitimer}, // set alarm clock - {"setpgid", LuaUnixSetpgid}, // set process group id for pid - {"setpgrp", LuaUnixSetpgrp}, // sets process group id - {"setresgid", LuaUnixSetresgid}, // sets real/effective/saved gids - {"setresuid", LuaUnixSetresuid}, // sets real/effective/saved uids - {"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs - {"setsid", LuaUnixSetsid}, // create a new session id - {"setsockopt", LuaUnixSetsockopt}, // tune socket options - {"setuid", LuaUnixSetuid}, // set real user id of process - {"shutdown", LuaUnixShutdown}, // make socket half empty or full - {"sigaction", LuaUnixSigaction}, // install signal handler + {"setfsgid", LuaUnixSetfsgid}, // set/get group id for fs ops + {"setfsuid", LuaUnixSetfsuid}, // set/get user id for fs ops + {"setgid", LuaUnixSetgid}, // set real group id of process + {"setitimer", LuaUnixSetitimer}, // set alarm clock + {"setpgid", LuaUnixSetpgid}, // set process group id for pid + {"setpgrp", LuaUnixSetpgrp}, // sets process group id + {"setresgid", LuaUnixSetresgid}, // sets real/effective/saved gids + {"setresuid", LuaUnixSetresuid}, // sets real/effective/saved uids + {"setrlimit", LuaUnixSetrlimit}, // prevent cpu memory bombs + {"setsid", LuaUnixSetsid}, // create a new session id + {"setsockopt", LuaUnixSetsockopt}, // tune socket options + {"setuid", LuaUnixSetuid}, // set real user id of process + {"shutdown", LuaUnixShutdown}, // make socket half empty or full + {"sigaction", LuaUnixSigaction}, // install signal handler {"sigprocmask", LuaUnixSigprocmask}, // change signal mask {"sigsuspend", LuaUnixSigsuspend}, // wait for signal {"siocgifconf", LuaUnixSiocgifconf}, // get list of network interfaces