From f968e2a72604774cc461bc6ffca39608120d98c1 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sun, 24 Jul 2022 02:56:03 -0700 Subject: [PATCH] Improve pledge() and unveil() further - Fix getpriority() - Add AT_MINSIGSTKSZ - Fix bugs in BPF code - Show more stuff in printargs.com - Write manual test for pledge.com - pledge() now generates tinier BPF code - Have pledge("exec") only enable execve() - Fix pledge.com chroot setuid functionality - Improve pledge.com unveiling of ape loader --- libc/calls/getpriority.c | 8 +- libc/calls/sched_getparam.c | 2 + libc/calls/sched_getscheduler.c | 14 +- libc/calls/sched_setscheduler.c | 10 +- libc/calls/setpriority.c | 6 +- libc/intrin/describeschedpolicy.c | 35 +- libc/mem/pledge.c | 716 ++++++++++++---------- libc/runtime/printargs.c | 25 + libc/sysv/consts.sh | 1 + libc/sysv/consts/AT_MINSIGSTKSZ.S | 2 + libc/sysv/consts/auxv.h | 42 +- test/libc/calls/printargs_test.c | 2 +- test/libc/calls/sched_setscheduler_test.c | 5 + test/libc/mem/pledge_test.c | 29 +- test/tool/build/pledge_test.sh | 175 ++++++ tool/build/pledge.c | 53 +- tool/net/help.txt | 9 +- 17 files changed, 722 insertions(+), 412 deletions(-) create mode 100644 libc/sysv/consts/AT_MINSIGSTKSZ.S create mode 100755 test/tool/build/pledge_test.sh diff --git a/libc/calls/getpriority.c b/libc/calls/getpriority.c index fb7a61967..cdb20e624 100644 --- a/libc/calls/getpriority.c +++ b/libc/calls/getpriority.c @@ -24,6 +24,10 @@ /** * Returns nice value of thing. * + * Since -1 might be a valid return value for this API, it's necessary + * to clear `errno` beforehand and see if it changed, in order to truly + * determine if an error happened. + * * @param which can be PRIO_PROCESS, PRIO_PGRP, PRIO_USER * @param who is the pid, pgid, or uid (0 means current) * @return value ∈ [-NZERO,NZERO) or -1 w/ errno @@ -32,7 +36,9 @@ int getpriority(int which, unsigned who) { int rc; if (!IsWindows()) { - rc = sys_getpriority(which, who) - 20; + if ((rc = sys_getpriority(which, who)) != -1) { + rc = 20 - rc; + } } else { rc = sys_getsetpriority_nt(which, who, 0, sys_getpriority_nt); } diff --git a/libc/calls/sched_getparam.c b/libc/calls/sched_getparam.c index 4482d81bc..85b91545b 100644 --- a/libc/calls/sched_getparam.c +++ b/libc/calls/sched_getparam.c @@ -21,6 +21,8 @@ /** * Gets scheduler policy parameter. + * + * @return 0 on success, or -1 w/ errno * @raise ENOSYS on XNU, Windows */ int sched_getparam(int pid, struct sched_param *param) { diff --git a/libc/calls/sched_getscheduler.c b/libc/calls/sched_getscheduler.c index e25f06764..d53a22808 100644 --- a/libc/calls/sched_getscheduler.c +++ b/libc/calls/sched_getscheduler.c @@ -20,12 +20,22 @@ #include "libc/calls/strace.internal.h" #include "libc/calls/struct/sched_param.h" #include "libc/dce.h" +#include "libc/intrin/describeflags.internal.h" /** * Gets scheduler policy for `pid`. * - * @param pid is id of process (where 0 is same as getpid()) + * @param pid is the id of the process whose scheduling policy should be + * queried. Setting `pid` to zero means the same thing as getpid(). + * This applies to all threads associated with the process. Linux is + * special; the kernel treats this as a thread id (noting that + * `getpid() == gettid()` is always the case on Linux for the main + * thread) and will only take effect for the specified tid. + * Therefore this function is POSIX-compliant iif `!__threaded`. * @return scheduler policy, or -1 w/ errno + * @error ESRCH if `pid` not found + * @error EPERM if not permitted + * @error EINVAL if `pid` is negative on Linux */ int sched_getscheduler(int pid) { int rc; @@ -34,6 +44,6 @@ int sched_getscheduler(int pid) { } else { rc = sys_sched_getscheduler(pid); } - STRACE("sched_getscheduler(%d) → %d% m", pid, rc); + STRACE("sched_getscheduler(%d) → %s% m", pid, DescribeSchedPolicy(rc)); return rc; } diff --git a/libc/calls/sched_setscheduler.c b/libc/calls/sched_setscheduler.c index a799be801..33323cda5 100644 --- a/libc/calls/sched_setscheduler.c +++ b/libc/calls/sched_setscheduler.c @@ -35,12 +35,12 @@ * before processes with numerically lower priority values. * * @param pid is the id of the process whose scheduling policy should be - * changed. This applies to all threads associated with the process. - * Linux is special; the kernel treats this as a thread id (noting - * that `getpid() == gettid()` is always the case on Linux for the - * main thread) and will only take effect for the specified tid. + * changed. Setting `pid` to zero means the same thing as getpid(). + * This applies to all threads associated with the process. Linux is + * special; the kernel treats this as a thread id (noting that + * `getpid() == gettid()` is always the case on Linux for the main + * thread) and will only take effect for the specified tid. * Therefore this function is POSIX-compliant iif `!__threaded`. - * Setting `pid` to zero means the same thing as getpid(). * * @param policy specifies the kernel's timesharing strategy. * diff --git a/libc/calls/setpriority.c b/libc/calls/setpriority.c index 005d8dac7..13eb826d4 100644 --- a/libc/calls/setpriority.c +++ b/libc/calls/setpriority.c @@ -27,12 +27,14 @@ * @param which can be PRIO_PROCESS, PRIO_PGRP, PRIO_USER * @param who is the pid, pgid, or uid, 0 meaning current * @param value ∈ [-NZERO,NZERO) which is clamped automatically - * @return nonzero on success or -1 w/ errno + * @return 0 on success or -1 w/ errno + * @error EACCES if lower that RLIMIT_NICE + * @error EACCES on Linux without CAP_SYS_NICE * @see getpriority(), nice() */ int setpriority(int which, unsigned who, int value) { if (!IsWindows()) { - return sys_setpriority(which, who, value); /* TODO(jart): -20 */ + return sys_setpriority(which, who, value); } else { return sys_getsetpriority_nt(which, who, value, sys_setpriority_nt); } diff --git a/libc/intrin/describeschedpolicy.c b/libc/intrin/describeschedpolicy.c index 529e5b661..f1348a66f 100644 --- a/libc/intrin/describeschedpolicy.c +++ b/libc/intrin/describeschedpolicy.c @@ -27,14 +27,29 @@ * Describes clock_gettime() clock argument. */ const char *(DescribeSchedPolicy)(char buf[48], int x) { - struct DescribeFlags flags[] = { - {SCHED_RESET_ON_FORK, "RESET_ON_FORK"}, // - {SCHED_OTHER, "OTHER"}, // - {SCHED_FIFO, "FIFO"}, // - {SCHED_RR, "RR"}, // - {SCHED_BATCH, "BATCH"}, // - {SCHED_IDLE, "IDLE"}, // - {SCHED_DEADLINE, "DEADLINE"}, // - }; - return DescribeFlags(buf, 48, flags, ARRAYLEN(flags), "SCHED_", x); + char *p = buf; + if (x == -1) { + goto DoNumber; + } + if (x & SCHED_RESET_ON_FORK) { + x &= ~SCHED_RESET_ON_FORK; + p = stpcpy(p, "SCHED_RESET_ON_FORK"); + } + if (x == SCHED_OTHER) { + stpcpy(p, "SCHED_OTHER"); + } else if (x == SCHED_FIFO) { + stpcpy(p, "SCHED_FIFO"); + } else if (x == SCHED_RR) { + stpcpy(p, "SCHED_RR"); + } else if (x == SCHED_BATCH) { + stpcpy(p, "SCHED_BATCH"); + } else if (x == SCHED_IDLE) { + stpcpy(p, "SCHED_IDLE"); + } else if (x == SCHED_DEADLINE) { + stpcpy(p, "SCHED_DEADLINE"); + } else { + DoNumber: + FormatInt32(p, x); + } + return buf; } diff --git a/libc/mem/pledge.c b/libc/mem/pledge.c index 25e6766a3..156ed8a4d 100644 --- a/libc/mem/pledge.c +++ b/libc/mem/pledge.c @@ -25,7 +25,6 @@ #include "libc/calls/syscall-sysv.internal.h" #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" @@ -44,16 +43,22 @@ #include "libc/sysv/consts/prot.h" #include "libc/sysv/errfuns.h" +#define SPECIAL 0xf000 #define ADDRLESS 0x2000 #define INET 0x8000 -#define LOCK 0x8000 +#define LOCK 0x4000 #define NOEXEC 0x8000 +#define EXEC 0x4000 #define READONLY 0x8000 +#define WRITEONLY 0x4000 +#define CREATONLY 0x2000 #define STDIO 0x8000 #define THREAD 0x8000 #define TTY 0x8000 #define UNIX 0x4000 -#define WRITEONLY 0x4000 +#define NOBITS 0x8000 +#define NOSIGSYS 0x8000 +#define RESTRICT 0x1000 // TODO(jart): fix chibicc #ifdef __chibicc__ @@ -70,7 +75,7 @@ struct Filter { }; static const uint16_t kPledgeLinuxDefault[] = { - __NR_linux_exit, // + __NR_linux_exit, // thread return / exit() }; // the stdio contains all the benign system calls. openbsd makes the @@ -80,115 +85,116 @@ static const uint16_t kPledgeLinuxDefault[] = { // difference in the latency of sched_yield() if it's at the start of // the bpf script or the end. static const uint16_t kPledgeLinuxStdio[] = { - __NR_linux_exit_group, // - __NR_linux_sched_yield, // - __NR_linux_clock_getres, // - __NR_linux_clock_gettime, // - __NR_linux_clock_nanosleep, // - __NR_linux_close_range, // - __NR_linux_close, // - __NR_linux_write, // - __NR_linux_writev, // - __NR_linux_pwrite, // - __NR_linux_pwritev, // - __NR_linux_pwritev2, // - __NR_linux_read, // - __NR_linux_readv, // - __NR_linux_pread, // - __NR_linux_preadv, // - __NR_linux_preadv2, // - __NR_linux_dup, // - __NR_linux_dup2, // - __NR_linux_dup3, // - __NR_linux_fchdir, // - __NR_linux_fcntl, // - __NR_linux_fstat, // - __NR_linux_fsync, // - __NR_linux_sysinfo, // - __NR_linux_fdatasync, // - __NR_linux_ftruncate, // - __NR_linux_getdents, // - __NR_linux_getegid, // - __NR_linux_getrandom, // - __NR_linux_getgroups, // - __NR_linux_getpgid, // - __NR_linux_getpgrp, // - __NR_linux_getpid, // - __NR_linux_gettid, // - __NR_linux_getuid, // - __NR_linux_getgid, // - __NR_linux_getsid, // - __NR_linux_getppid, // - __NR_linux_geteuid, // - __NR_linux_getrlimit, // - __NR_linux_getresgid, // - __NR_linux_getresuid, // - __NR_linux_getitimer, // - __NR_linux_setitimer, // - __NR_linux_timerfd_create, // - __NR_linux_timerfd_settime, // - __NR_linux_timerfd_gettime, // - __NR_linux_copy_file_range, // - __NR_linux_gettimeofday, // - __NR_linux_sendfile, // - __NR_linux_vmsplice, // - __NR_linux_splice, // - __NR_linux_lseek, // - __NR_linux_tee, // - __NR_linux_brk, // - __NR_linux_msync, // - __NR_linux_mmap | NOEXEC, // - __NR_linux_mremap, // - __NR_linux_munmap, // - __NR_linux_mincore, // - __NR_linux_madvise, // - __NR_linux_fadvise, // - __NR_linux_mprotect | NOEXEC, // - __NR_linux_arch_prctl, // - __NR_linux_migrate_pages, // - __NR_linux_sync_file_range, // - __NR_linux_set_tid_address, // - __NR_linux_nanosleep, // - __NR_linux_pipe, // - __NR_linux_pipe2, // - __NR_linux_poll, // - __NR_linux_ppoll, // - __NR_linux_select, // - __NR_linux_pselect6, // - __NR_linux_epoll_create, // - __NR_linux_epoll_create1, // - __NR_linux_epoll_ctl, // - __NR_linux_epoll_wait, // - __NR_linux_epoll_pwait, // - __NR_linux_epoll_pwait2, // - __NR_linux_recvfrom, // - __NR_linux_sendto | ADDRLESS, // - __NR_linux_ioctl, // - __NR_linux_alarm, // - __NR_linux_pause, // - __NR_linux_shutdown, // - __NR_linux_eventfd, // - __NR_linux_eventfd2, // - __NR_linux_signalfd, // - __NR_linux_signalfd4, // - __NR_linux_sigaction, // - __NR_linux_sigaltstack, // - __NR_linux_sigprocmask, // - __NR_linux_sigsuspend, // - __NR_linux_sigreturn, // - __NR_linux_sigpending, // - __NR_linux_socketpair, // - __NR_linux_getrusage, // - __NR_linux_times, // - __NR_linux_umask, // - __NR_linux_wait4, // - __NR_linux_uname, // - __NR_linux_prctl, // - __NR_linux_clone | THREAD, // - __NR_linux_futex, // - __NR_linux_set_robust_list, // - __NR_linux_get_robust_list, // - __NR_linux_prlimit | STDIO, // + __NR_linux_exit_group, // + __NR_linux_sched_yield, // + __NR_linux_sched_getaffinity, // + __NR_linux_clock_getres, // + __NR_linux_clock_gettime, // + __NR_linux_clock_nanosleep, // + __NR_linux_close_range, // + __NR_linux_close, // + __NR_linux_write, // + __NR_linux_writev, // + __NR_linux_pwrite, // + __NR_linux_pwritev, // + __NR_linux_pwritev2, // + __NR_linux_read, // + __NR_linux_readv, // + __NR_linux_pread, // + __NR_linux_preadv, // + __NR_linux_preadv2, // + __NR_linux_dup, // + __NR_linux_dup2, // + __NR_linux_dup3, // + __NR_linux_fchdir, // + __NR_linux_fcntl | STDIO, // + __NR_linux_fstat, // + __NR_linux_fsync, // + __NR_linux_sysinfo, // + __NR_linux_fdatasync, // + __NR_linux_ftruncate, // + __NR_linux_getdents, // + __NR_linux_getrandom, // + __NR_linux_getgroups, // + __NR_linux_getpgid, // + __NR_linux_getpgrp, // + __NR_linux_getpid, // + __NR_linux_gettid, // + __NR_linux_getuid, // + __NR_linux_getgid, // + __NR_linux_getsid, // + __NR_linux_getppid, // + __NR_linux_geteuid, // + __NR_linux_getegid, // + __NR_linux_getrlimit, // + __NR_linux_getresgid, // + __NR_linux_getresuid, // + __NR_linux_getitimer, // + __NR_linux_setitimer, // + __NR_linux_timerfd_create, // + __NR_linux_timerfd_settime, // + __NR_linux_timerfd_gettime, // + __NR_linux_copy_file_range, // + __NR_linux_gettimeofday, // + __NR_linux_sendfile, // + __NR_linux_vmsplice, // + __NR_linux_splice, // + __NR_linux_lseek, // + __NR_linux_tee, // + __NR_linux_brk, // + __NR_linux_msync, // + __NR_linux_mmap | NOEXEC, // + __NR_linux_mremap, // + __NR_linux_munmap, // + __NR_linux_mincore, // + __NR_linux_madvise, // + __NR_linux_fadvise, // + __NR_linux_mprotect | NOEXEC, // + __NR_linux_arch_prctl, // + __NR_linux_migrate_pages, // + __NR_linux_sync_file_range, // + __NR_linux_set_tid_address, // + __NR_linux_nanosleep, // + __NR_linux_pipe, // + __NR_linux_pipe2, // + __NR_linux_poll, // + __NR_linux_ppoll, // + __NR_linux_select, // + __NR_linux_pselect6, // + __NR_linux_epoll_create, // + __NR_linux_epoll_create1, // + __NR_linux_epoll_ctl, // + __NR_linux_epoll_wait, // + __NR_linux_epoll_pwait, // + __NR_linux_epoll_pwait2, // + __NR_linux_recvfrom, // + __NR_linux_sendto | ADDRLESS, // + __NR_linux_ioctl | RESTRICT, // + __NR_linux_alarm, // + __NR_linux_pause, // + __NR_linux_shutdown, // + __NR_linux_eventfd, // + __NR_linux_eventfd2, // + __NR_linux_signalfd, // + __NR_linux_signalfd4, // + __NR_linux_sigaction | NOSIGSYS, // + __NR_linux_sigaltstack, // + __NR_linux_sigprocmask, // + __NR_linux_sigsuspend, // + __NR_linux_sigreturn, // + __NR_linux_sigpending, // + __NR_linux_socketpair, // + __NR_linux_getrusage, // + __NR_linux_times, // + __NR_linux_umask, // + __NR_linux_wait4, // + __NR_linux_uname, // + __NR_linux_prctl | STDIO, // + __NR_linux_clone | THREAD, // + __NR_linux_futex, // + __NR_linux_set_robust_list, // + __NR_linux_get_robust_list, // + __NR_linux_prlimit | STDIO, // }; static const uint16_t kPledgeLinuxFlock[] = { @@ -226,26 +232,26 @@ static const uint16_t kPledgeLinuxWpath[] = { __NR_linux_faccessat, // __NR_linux_faccessat2, // __NR_linux_readlinkat, // - __NR_linux_chmod, // - __NR_linux_fchmod, // - __NR_linux_fchmodat, // + __NR_linux_chmod | NOBITS, // + __NR_linux_fchmod | NOBITS, // + __NR_linux_fchmodat | NOBITS, // }; static const uint16_t kPledgeLinuxCpath[] = { - __NR_linux_open, // - __NR_linux_openat, // - __NR_linux_rename, // - __NR_linux_renameat, // - __NR_linux_renameat2, // - __NR_linux_link, // - __NR_linux_linkat, // - __NR_linux_symlink, // - __NR_linux_symlinkat, // - __NR_linux_rmdir, // - __NR_linux_unlink, // - __NR_linux_unlinkat, // - __NR_linux_mkdir, // - __NR_linux_mkdirat, // + __NR_linux_open | CREATONLY, // + __NR_linux_openat | CREATONLY, // + __NR_linux_rename, // + __NR_linux_renameat, // + __NR_linux_renameat2, // + __NR_linux_link, // + __NR_linux_linkat, // + __NR_linux_symlink, // + __NR_linux_symlinkat, // + __NR_linux_rmdir, // + __NR_linux_unlink, // + __NR_linux_unlinkat, // + __NR_linux_mkdir, // + __NR_linux_mkdirat, // }; static const uint16_t kPledgeLinuxDpath[] = { @@ -254,49 +260,53 @@ static const uint16_t kPledgeLinuxDpath[] = { }; static const uint16_t kPledgeLinuxFattr[] = { - __NR_linux_chmod, // - __NR_linux_fchmod, // - __NR_linux_fchmodat, // - __NR_linux_utime, // - __NR_linux_utimes, // - __NR_linux_futimesat, // - __NR_linux_utimensat, // + __NR_linux_chmod | NOBITS, // + __NR_linux_fchmod | NOBITS, // + __NR_linux_fchmodat | NOBITS, // + __NR_linux_utime, // + __NR_linux_utimes, // + __NR_linux_futimesat, // + __NR_linux_utimensat, // }; static const uint16_t kPledgeLinuxInet[] = { - __NR_linux_socket | INET, // - __NR_linux_listen, // - __NR_linux_bind, // - __NR_linux_sendto, // - __NR_linux_connect, // - __NR_linux_accept, // - __NR_linux_accept4, // - __NR_linux_getsockopt, // - __NR_linux_setsockopt, // - __NR_linux_getpeername, // - __NR_linux_getsockname, // + __NR_linux_socket | INET, // + __NR_linux_listen, // + __NR_linux_bind, // + __NR_linux_sendto, // + __NR_linux_connect, // + __NR_linux_accept, // + __NR_linux_accept4, // + __NR_linux_getsockopt | RESTRICT, // + __NR_linux_setsockopt | RESTRICT, // + __NR_linux_getpeername, // + __NR_linux_getsockname, // }; static const uint16_t kPledgeLinuxUnix[] = { - __NR_linux_socket | UNIX, // - __NR_linux_listen, // - __NR_linux_bind, // - __NR_linux_connect, // - __NR_linux_sendto, // - __NR_linux_accept, // - __NR_linux_accept4, // - __NR_linux_getsockopt, // - __NR_linux_setsockopt, // - __NR_linux_getpeername, // - __NR_linux_getsockname, // + __NR_linux_socket | UNIX, // + __NR_linux_listen, // + __NR_linux_bind, // + __NR_linux_connect, // + __NR_linux_sendto, // + __NR_linux_accept, // + __NR_linux_accept4, // + __NR_linux_getsockopt | RESTRICT, // + __NR_linux_setsockopt | RESTRICT, // + __NR_linux_getpeername, // + __NR_linux_getsockname, // }; static const uint16_t kPledgeLinuxDns[] = { - __NR_linux_socket | INET, // - __NR_linux_bind, // - __NR_linux_sendto, // - __NR_linux_connect, // - __NR_linux_recvfrom, // + __NR_linux_socket | INET, // + __NR_linux_bind, // + __NR_linux_sendto, // + __NR_linux_connect, // + __NR_linux_recvfrom, // + __NR_linux_fstatat, // + __NR_linux_openat | READONLY, // + __NR_linux_read, // + __NR_linux_close, // }; static const uint16_t kPledgeLinuxTty[] = { @@ -314,19 +324,27 @@ static const uint16_t kPledgeLinuxSendfd[] = { }; static const uint16_t kPledgeLinuxProc[] = { - __NR_linux_fork, // - __NR_linux_vfork, // - __NR_linux_clone, // - __NR_linux_kill, // - __NR_linux_setsid, // - __NR_linux_setpgid, // - __NR_linux_prlimit, // - __NR_linux_setrlimit, // - __NR_linux_getpriority, // - __NR_linux_setpriority, // - __NR_linux_ioprio_get, // - __NR_linux_ioprio_set, // - __NR_linux_tgkill, // + __NR_linux_fork, // + __NR_linux_vfork, // + __NR_linux_clone | RESTRICT, // + __NR_linux_kill, // + __NR_linux_setsid, // + __NR_linux_setpgid, // + __NR_linux_prlimit, // + __NR_linux_setrlimit, // + __NR_linux_getpriority, // + __NR_linux_setpriority, // + __NR_linux_ioprio_get, // + __NR_linux_ioprio_set, // + __NR_linux_sched_getscheduler, // + __NR_linux_sched_setscheduler, // + __NR_linux_sched_get_priority_min, // + __NR_linux_sched_get_priority_max, // + __NR_linux_sched_getaffinity, // + __NR_linux_sched_setaffinity, // + __NR_linux_sched_getparam, // + __NR_linux_sched_setparam, // + __NR_linux_tgkill, // }; static const uint16_t kPledgeLinuxId[] = { @@ -351,20 +369,11 @@ static const uint16_t kPledgeLinuxSettime[] = { }; static const uint16_t kPledgeLinuxProtExec[] = { - __NR_linux_mmap, // - __NR_linux_mprotect, // + __NR_linux_mmap | EXEC, // + __NR_linux_mprotect, // }; static const uint16_t kPledgeLinuxExec[] = { - __NR_linux_execve, // - __NR_linux_execveat, // - __NR_linux_access, // for ape loader - __NR_linux_faccessat, // for ape binaries - __NR_linux_open | READONLY, // for ape loader - __NR_linux_openat | READONLY, // for ape binaries -}; - -static const uint16_t kPledgeLinuxExec2[] = { __NR_linux_execve, // __NR_linux_execveat, // }; @@ -473,23 +482,13 @@ static bool AppendOriginVerification(struct Filter *f, long ipromises) { return AppendFilter(f, PLEDGE(fragment)); } -// Authorize specific system call w/o considering its arguments. -static bool AllowSyscall(struct Filter *f, uint16_t w) { - struct sock_filter fragment[] = { - /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, w, 0, 2 - 1), - /*L1*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - /*L2*/ /* next filter */ - }; - return AppendFilter(f, PLEDGE(fragment)); -} - // The first argument of sys_clone_linux() must NOT have: // // - CLONE_NEWNS (0x00020000) // - CLONE_PTRACE (0x00002000) // - CLONE_UNTRACED (0x00800000) // -static bool AllowClone(struct Filter *f) { +static bool AllowCloneRestrict(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_clone, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])), @@ -539,14 +538,14 @@ static bool AllowCloneThread(struct Filter *f) { // - FIOCLEX (0x5451) // - FIONCLEX (0x5450) // -static bool AllowIoctl(struct Filter *f) { +static bool AllowIoctlStdio(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_ioctl, 0, 8 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), - /*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x541b, 6 - 3, 0), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5421, 6 - 4, 0), - /*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5451, 6 - 5, 0), - /*L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5450, 0, 7 - 6), + /*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x541b, 3, 0), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5421, 2, 0), + /*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5451, 1, 0), + /*L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5450, 0, 1), /*L6*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L7*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L8*/ /* next filter */ @@ -573,17 +572,17 @@ static bool AllowIoctlTty(struct Filter *f) { static const struct sock_filter fragment[] = { /* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_ioctl, 0, 16 - 1), /* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), - /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5401, 14 - 3, 0), - /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5402, 14 - 4, 0), - /* L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5403, 14 - 5, 0), - /* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5404, 14 - 6, 0), - /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5413, 14 - 7, 0), - /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5410, 14 - 8, 0), - /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x540f, 14 - 9, 0), - /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5414, 14 - 10, 0), - /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x540b, 14 - 11, 0), - /*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x540a, 14 - 12, 0), - /*L12*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5409, 14 - 13, 0), + /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5401, 11, 0), + /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5402, 10, 0), + /* L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5403, 9, 0), + /* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5404, 8, 0), + /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5413, 7, 0), + /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5410, 6, 0), + /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x540f, 5, 0), + /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5414, 4, 0), + /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x540b, 3, 0), + /*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x540a, 2, 0), + /*L12*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5409, 1, 0), /*L13*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x5427, 0, 1), /*L14*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L15*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), @@ -600,45 +599,45 @@ static bool AllowIoctlTty(struct Filter *f) { // // The optname argument of setsockopt() must be one of: // -// - TCP_NODELAY ( 1) -// - TCP_CORK ( 3) -// - TCP_KEEPIDLE ( 4) -// - TCP_KEEPINTVL ( 5) -// - SO_TYPE ( 3) -// - SO_ERROR ( 4) -// - SO_DONTROUTE ( 5) -// - SO_REUSEPORT (15) -// - SO_REUSEADDR ( 2) -// - SO_KEEPALIVE ( 9) -// - SO_RCVTIMEO (20) -// - SO_SNDTIMEO (21) -// - IP_RECVTTL (12) -// - IP_RECVERR (11) -// - TCP_FASTOPEN (23) -// - TCP_FASTOPEN_CONNECT (30) +// - TCP_NODELAY (0x01) +// - TCP_CORK (0x03) +// - TCP_KEEPIDLE (0x04) +// - TCP_KEEPINTVL (0x05) +// - SO_TYPE (0x03) +// - SO_ERROR (0x04) +// - SO_DONTROUTE (0x05) +// - SO_REUSEPORT (0x0f) +// - SO_REUSEADDR (0x02) +// - SO_KEEPALIVE (0x09) +// - SO_RCVTIMEO (0x14) +// - SO_SNDTIMEO (0x15) +// - IP_RECVTTL (0x0c) +// - IP_RECVERR (0x0b) +// - TCP_FASTOPEN (0x17) +// - TCP_FASTOPEN_CONNECT (0x1e) // -static bool AllowSetsockopt(struct Filter *f) { +static bool AllowSetsockoptRestrict(struct Filter *f) { static const int nr = __NR_linux_setsockopt; static const struct sock_filter fragment[] = { /* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, nr, 0, 21 - 1), /* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), - /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 5 - 3, 0), - /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 5 - 4, 0), + /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 2, 0), + /* 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, 3, 19 - 7, 0), - /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 12, 19 - 8, 0), - /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 19, 19 - 9, 0), - /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 19 - 10, 0), - /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 9, 19 - 11, 0), - /*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 20, 19 - 12, 0), - /*L12*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 19 - 13, 0), - /*L13*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 11, 19 - 14, 0), - /*L14*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 4, 19 - 15, 0), - /*L15*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 5, 19 - 16, 0), - /*L16*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 23, 19 - 17, 0), - /*L17*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 30, 19 - 18, 0), - /*L18*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 21, 0, 20 - 19), + /* 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), + /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 9, 0), + /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x09, 8, 0), + /*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x14, 7, 0), + /*L12*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x01, 6, 0), + /*L13*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0b, 5, 0), + /*L14*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x04, 4, 0), + /*L15*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x05, 3, 0), + /*L16*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x17, 2, 0), + /*L17*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x1e, 1, 0), + /*L18*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x15, 0, 1), /*L19*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L20*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L21*/ /* next filter */ @@ -653,27 +652,27 @@ static bool AllowSetsockopt(struct Filter *f) { // // The optname argument of getsockopt() must be one of: // -// - SO_TYPE ( 3) -// - SO_REUSEPORT (15) -// - SO_REUSEADDR ( 2) -// - SO_KEEPALIVE ( 9) -// - SO_RCVTIMEO (20) -// - SO_SNDTIMEO (21) +// - SO_TYPE (0x03) +// - SO_REUSEPORT (0x0f) +// - SO_REUSEADDR (0x02) +// - SO_KEEPALIVE (0x09) +// - SO_RCVTIMEO (0x14) +// - SO_SNDTIMEO (0x15) // -static bool AllowGetsockopt(struct Filter *f) { +static bool AllowGetsockoptRestrict(struct Filter *f) { static const int nr = __NR_linux_getsockopt; static const struct sock_filter fragment[] = { /* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, nr, 0, 13 - 1), /* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), - /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 11 - 3, 0), - /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6, 11 - 4, 0), + /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 1, 0), + /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6, 0, 12 - 4), /* L4*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), - /* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, 11 - 6, 0), - /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 15, 11 - 7, 0), - /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 11 - 8, 0), - /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 9, 11 - 9, 0), - /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 20, 11 - 10, 0), - /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 21, 0, 12 - 11), + /* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x03, 5, 0), + /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0f, 4, 0), + /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 3, 0), + /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x09, 2, 0), + /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x14, 1, 0), + /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x15, 0, 1), /*L11*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L12*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L13*/ /* next filter */ @@ -687,7 +686,7 @@ static bool AllowGetsockopt(struct Filter *f) { // - MAP_NONBLOCK (0x10000) // - MAP_HUGETLB (0x40000) // -static bool AllowMmap(struct Filter *f) { +static bool AllowMmapExec(struct Filter *f) { intptr_t y = (intptr_t)__privileged_end; assert(0 < y && y < INT_MAX); struct sock_filter fragment[] = { @@ -723,7 +722,7 @@ static bool AllowMmapNoexec(struct Filter *f) { /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 8 - 4), /*L4*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[3])), // flags /*L5*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x5a000), - /*L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 8 - 7), + /*L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L7*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L8*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L9*/ /* next filter */ @@ -742,7 +741,7 @@ static bool AllowMprotectNoexec(struct Filter *f) { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_mprotect, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), // prot /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~(PROT_READ | PROT_WRITE)), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -759,7 +758,7 @@ static bool AllowOpenReadonly(struct Filter *f) { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_open, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, O_ACCMODE), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, O_RDONLY, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, O_RDONLY, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -777,7 +776,7 @@ static bool AllowOpenatReadonly(struct Filter *f) { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_openat, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, O_ACCMODE), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, O_RDONLY, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, O_RDONLY, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -795,7 +794,7 @@ static bool AllowOpenWriteonly(struct Filter *f) { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_open, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 020200100), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -813,7 +812,7 @@ static bool AllowOpenatWriteonly(struct Filter *f) { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_openat, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 020200100), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -832,7 +831,7 @@ static bool AllowOpenatWriteonly(struct Filter *f) { // - S_ISGID (02000 setgid) // - S_ISUID (04000 setuid) // -static bool AllowOpen(struct Filter *f) { +static bool AllowOpenCreatonly(struct Filter *f) { static const struct sock_filter fragment[] = { /* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_open, 0, 12 - 1), /* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), @@ -843,7 +842,7 @@ static bool AllowOpen(struct Filter *f) { /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 020200000, 0, 10 - 7), /* L7*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), /* L8*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 07000), - /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 11 - 10), + /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L10*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L11*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L12*/ /* next filter */ @@ -862,7 +861,7 @@ static bool AllowOpen(struct Filter *f) { // - S_ISGID (02000 setgid) // - S_ISUID (04000 setuid) // -static bool AllowOpenat(struct Filter *f) { +static bool AllowOpenatCreatonly(struct Filter *f) { static const struct sock_filter fragment[] = { /* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_openat, 0, 12 - 1), /* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), @@ -873,7 +872,7 @@ static bool AllowOpenat(struct Filter *f) { /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 020200000, 0, 10 - 7), /* L7*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[3])), /* L8*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 07000), - /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 11 - 10), + /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L10*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L11*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L12*/ /* next filter */ @@ -890,7 +889,7 @@ static bool AllowOpenat(struct Filter *f) { // - F_GETFL (3) // - F_SETFL (4) // -static bool AllowFcntl(struct Filter *f) { +static bool AllowFcntlStdio(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_fcntl, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), @@ -932,7 +931,7 @@ static bool AllowSendtoAddrless(struct Filter *f) { /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[4]) + 0), /*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 6 - 3), /*L3*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[4]) + 4), - /*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 6 - 3), + /*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 6 - 5), /*L5*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L6*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L7*/ /* next filter */ @@ -944,12 +943,12 @@ static bool AllowSendtoAddrless(struct Filter *f) { // // - SIGSYS (31) // -static bool AllowSigaction(struct Filter *f) { +static bool AllowSigactionNosigsys(struct Filter *f) { static const int nr = __NR_linux_sigaction; static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, nr, 0, 5 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])), - /*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 31, 4 - 3, 0), + /*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 31, 1, 0), /*L3*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L4*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L5*/ /* next filter */ @@ -959,8 +958,8 @@ static bool AllowSigaction(struct Filter *f) { // The family parameter of socket() must be one of: // -// - AF_INET (2) -// - AF_INET6 (10) +// - AF_INET (0x02) +// - AF_INET6 (0x0a) // // The type parameter of socket() will ignore: // @@ -969,31 +968,31 @@ static bool AllowSigaction(struct Filter *f) { // // The type parameter of socket() must be one of: // -// - SOCK_STREAM (1) -// - SOCK_DGRAM (2) +// - SOCK_STREAM (0x01) +// - SOCK_DGRAM (0x02) // // The protocol parameter of socket() must be one of: // // - 0 -// - IPPROTO_ICMP (1) -// - IPPROTO_TCP (6) -// - IPPROTO_UDP (17) +// - IPPROTO_ICMP (0x01) +// - IPPROTO_TCP (0x06) +// - IPPROTO_UDP (0x11) // static bool AllowSocketInet(struct Filter *f) { static const struct sock_filter fragment[] = { /* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_socket, 0, 15 - 1), /* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])), - /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 4 - 3, 0), - /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 10, 0, 14 - 4), + /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 1, 0), + /* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0a, 0, 14 - 4), /* L4*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), /* L5*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~0x80800), - /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 8 - 7, 0), - /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 0, 14 - 8), + /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x01, 1, 0), + /* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x02, 0, 14 - 8), /* L8*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), - /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 13 - 10, 0), - /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 13 - 11, 0), - /*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6, 13 - 12, 0), - /*L12*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 17, 0, 14 - 12), + /* L9*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x00, 3, 0), + /*L10*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x01, 2, 0), + /*L11*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x06, 1, 0), + /*L12*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x11, 0, 1), /*L13*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L14*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L15*/ /* next filter */ @@ -1027,10 +1026,10 @@ static bool AllowSocketUnix(struct Filter *f) { /* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 10 - 3), /* L3*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), /* L5*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, ~0x80800), - /* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 7 - 6, 0), + /* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 1, 0), /* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 2, 0, 10 - 7), /* L7*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), - /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 10 - 9), + /* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /* L9*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L10*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L11*/ /* next filter */ @@ -1047,7 +1046,7 @@ static bool AllowSocketUnix(struct Filter *f) { // - PR_SET_NO_NEW_PRIVS (38) // - PR_CAPBSET_READ (23) // -static bool AllowPrctl(struct Filter *f) { +static bool AllowPrctlStdio(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_prctl, 0, 10 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])), @@ -1070,12 +1069,12 @@ static bool AllowPrctl(struct Filter *f) { // - S_ISGID (02000 setgid) // - S_ISUID (04000 setuid) // -static bool AllowChmod(struct Filter *f) { +static bool AllowChmodNobits(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_chmod, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 07000), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -1089,12 +1088,12 @@ static bool AllowChmod(struct Filter *f) { // - S_ISGID (02000 setgid) // - S_ISUID (04000 setuid) // -static bool AllowFchmod(struct Filter *f) { +static bool AllowFchmodNobits(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_fchmod, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 07000), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -1108,12 +1107,12 @@ static bool AllowFchmod(struct Filter *f) { // - S_ISGID (02000 setgid) // - S_ISUID (04000 setuid) // -static bool AllowFchmodat(struct Filter *f) { +static bool AllowFchmodatNobits(struct Filter *f) { static const struct sock_filter fragment[] = { /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_fchmodat, 0, 6 - 1), /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), /*L2*/ BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 07000), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 5 - 4), + /*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L6*/ /* next filter */ @@ -1131,7 +1130,7 @@ static bool AllowPrlimitStdio(struct Filter *f) { /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2])), /*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 6 - 3), /*L3*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[2]) + 4), - /*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 6 - 5), + /*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, 0, 1), /*L5*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /*L6*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), /*L7*/ /* next filter */ @@ -1139,12 +1138,55 @@ static bool AllowPrlimitStdio(struct Filter *f) { return AppendFilter(f, PLEDGE(fragment)); } +static int CountUnspecial(const uint16_t *p, size_t len) { + int i, count; + for (count = i = 0; i < len; ++i) { + if (!(p[i] & SPECIAL)) { + ++count; + } + } + return count; +} + static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { - int i; + int i, j, count; + + // handle ordinals which allow syscalls regardless of args + // we put in extra effort here to reduce num of bpf instrs + if ((count = CountUnspecial(p, len))) { + if (count < 256) { + for (j = i = 0; i < len; ++i) { + if (p[i] & SPECIAL) continue; + // jump to ALLOW rule below if accumulator equals ordinal + struct sock_filter fragment[] = { + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, // instruction + p[i], // operand + count - j - 1, // jump if true displacement + j == count - 1), // jump if false displacement + }; + if (!AppendFilter(f, PLEDGE(fragment))) { + return false; + } + ++j; + } + struct sock_filter fragment[] = { + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + }; + if (!AppendFilter(f, PLEDGE(fragment))) { + return false; + } + } else { + asm("ud2"); // list of ordinals exceeds max displacement + unreachable; + } + } + + // handle "special" ordinals which use hand-crafted bpf for (i = 0; i < len; ++i) { + if (!(p[i] & SPECIAL)) continue; switch (p[i]) { - case __NR_linux_mmap: - if (!AllowMmap(f)) return false; + case __NR_linux_mmap | EXEC: + if (!AllowMmapExec(f)) return false; break; case __NR_linux_mmap | NOEXEC: if (!AllowMmapNoexec(f)) return false; @@ -1152,26 +1194,26 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { case __NR_linux_mprotect | NOEXEC: if (!AllowMprotectNoexec(f)) return false; break; - case __NR_linux_chmod: - if (!AllowChmod(f)) return false; + case __NR_linux_chmod | NOBITS: + if (!AllowChmodNobits(f)) return false; break; - case __NR_linux_fchmod: - if (!AllowFchmod(f)) return false; + case __NR_linux_fchmod | NOBITS: + if (!AllowFchmodNobits(f)) return false; break; - case __NR_linux_fchmodat: - if (!AllowFchmodat(f)) return false; + case __NR_linux_fchmodat | NOBITS: + if (!AllowFchmodatNobits(f)) return false; break; - case __NR_linux_sigaction: - if (!AllowSigaction(f)) return false; + case __NR_linux_sigaction | NOSIGSYS: + if (!AllowSigactionNosigsys(f)) return false; break; - case __NR_linux_prctl: - if (!AllowPrctl(f)) return false; + case __NR_linux_prctl | STDIO: + if (!AllowPrctlStdio(f)) return false; break; - case __NR_linux_open: - if (!AllowOpen(f)) return false; + case __NR_linux_open | CREATONLY: + if (!AllowOpenCreatonly(f)) return false; break; - case __NR_linux_openat: - if (!AllowOpenat(f)) return false; + case __NR_linux_openat | CREATONLY: + if (!AllowOpenatCreatonly(f)) return false; break; case __NR_linux_open | READONLY: if (!AllowOpenReadonly(f)) return false; @@ -1185,20 +1227,20 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { case __NR_linux_openat | WRITEONLY: if (!AllowOpenatWriteonly(f)) return false; break; - case __NR_linux_setsockopt: - if (!AllowSetsockopt(f)) return false; + case __NR_linux_setsockopt | RESTRICT: + if (!AllowSetsockoptRestrict(f)) return false; break; - case __NR_linux_getsockopt: - if (!AllowGetsockopt(f)) return false; + case __NR_linux_getsockopt | RESTRICT: + if (!AllowGetsockoptRestrict(f)) return false; break; - case __NR_linux_fcntl: - if (!AllowFcntl(f)) return false; + case __NR_linux_fcntl | STDIO: + if (!AllowFcntlStdio(f)) return false; break; case __NR_linux_fcntl | LOCK: if (!AllowFcntlLock(f)) return false; break; - case __NR_linux_ioctl: - if (!AllowIoctl(f)) return false; + case __NR_linux_ioctl | RESTRICT: + if (!AllowIoctlStdio(f)) return false; break; case __NR_linux_ioctl | TTY: if (!AllowIoctlTty(f)) return false; @@ -1212,8 +1254,8 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { case __NR_linux_sendto | ADDRLESS: if (!AllowSendtoAddrless(f)) return false; break; - case __NR_linux_clone: - if (!AllowClone(f)) return false; + case __NR_linux_clone | RESTRICT: + if (!AllowCloneRestrict(f)) return false; break; case __NR_linux_clone | THREAD: if (!AllowCloneThread(f)) return false; @@ -1222,11 +1264,11 @@ static bool AppendPledge(struct Filter *f, const uint16_t *p, size_t len) { if (!AllowPrlimitStdio(f)) return false; break; default: - assert(~p[i] & ~0xfff); - if (!AllowSyscall(f, p[i])) return false; - break; + asm("ud2"); // switch forgot to define a special ordinal + unreachable; } } + return true; } @@ -1416,11 +1458,11 @@ int ParsePromises(const char *promises, unsigned long *out) { * * - "settime" allows settimeofday and clock_adjtime. * - * - "exec" allows execve, execveat, access, openat(O_RDONLY). If the - * executable in question needs a loader, then you may need prot_exec - * too. With APE, security will be stronger if you assimilate your - * binaries beforehand, using the --assimilate flag, or the - * o//tool/build/assimilate.com program. + * - "exec" allows execve, execveat. If the executable in question needs + * a loader, then you'll need rpath and prot_exec too. However that's + * not needed if you assimilate your APE binary beforehand, because + * security is strongest for static binaries; use the --assimilate + * flag or o//tool/build/assimilate.com program. * * - "prot_exec" allows mmap(PROT_EXEC) and mprotect(PROT_EXEC). This is * needed to (1) code morph mutexes in __enable_threads(), and it's diff --git a/libc/runtime/printargs.c b/libc/runtime/printargs.c index 31533a09a..77cc9ddc6 100644 --- a/libc/runtime/printargs.c +++ b/libc/runtime/printargs.c @@ -19,6 +19,7 @@ #include "libc/calls/calls.h" #include "libc/calls/strace.internal.h" #include "libc/calls/struct/rlimit.h" +#include "libc/calls/struct/sched_param.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/termios.h" #include "libc/calls/struct/utsname.h" @@ -29,6 +30,7 @@ #include "libc/errno.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/kprintf.h" +#include "libc/intrin/promises.internal.h" #include "libc/macros.internal.h" #include "libc/nexgen32e/cpuid4.internal.h" #include "libc/nexgen32e/kcpuids.h" @@ -52,6 +54,7 @@ #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/pr.h" +#include "libc/sysv/consts/prio.h" #include "libc/sysv/consts/rlim.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/termios.h" @@ -107,6 +110,7 @@ static const struct AuxiliaryValue { {"%-14p", &AT_TIMEKEEP, "AT_TIMEKEEP"}, {"%-14p", &AT_STACKPROT, "AT_STACKPROT"}, {"%-14p", &AT_EHDRFLAGS, "AT_EHDRFLAGS"}, + {"%-14d", &AT_MINSIGSTKSZ, "AT_MINSIGSTKSZ"}, }; static const char *FindNameById(const struct IdName *names, unsigned long id) { @@ -161,6 +165,7 @@ textstartup void __printargs(const char *prologue) { uintptr_t *auxp; struct rlimit rlim; struct utsname uts; + struct sched_param sp; struct termios termios; struct AuxiliaryValue *auxinfo; union { @@ -168,6 +173,8 @@ textstartup void __printargs(const char *prologue) { struct pollfd pfds[128]; } u; + if (!PLEDGED(STDIO)) return; + --__ftrace; --__strace; e = errno; @@ -296,6 +303,24 @@ textstartup void __printargs(const char *prologue) { PRINT(" error: sigprocmask() failed %m"); } + if (PLEDGED(PROC)) { + PRINT(""); + PRINT("SCHEDULER"); + errno = 0; + PRINT(" ☼ getpriority(PRIO_PROCESS) → %d% m", getpriority(PRIO_PROCESS, 0)); + errno = 0; + PRINT(" ☼ getpriority(PRIO_PGRP) → %d% m", getpriority(PRIO_PGRP, 0)); + errno = 0; + PRINT(" ☼ getpriority(PRIO_USER) → %d% m", getpriority(PRIO_USER, 0)); + errno = 0; + PRINT(" ☼ sched_getscheduler() → %s% m", + DescribeSchedPolicy(sched_getscheduler(0))); + errno = 0; + if (sched_getparam(0, &sp) != -1) { + PRINT(" ☼ sched_getparam() → %d% m", sp.sched_priority); + } + } + if (IsLinux()) { PRINT(""); PRINT("CAPABILITIES"); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index f7a1913ff..cc03f446f 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -464,6 +464,7 @@ syscon auxv AT_EXECFN 31 31 15 999 2014 31 # address of string co syscon auxv AT_SYSINFO_EHDR 33 0 0 0 0 0 syscon auxv AT_STACKBASE 0 0 0 0 13 0 syscon auxv AT_EXECPATH 31 31 15 999 2014 31 # FreeBSD name for AT_EXECFN +syscon auxv AT_MINSIGSTKSZ 51 0 0 0 0 0 # FreeBSD name for AT_EXECFN syscon auxv AT_CANARY 0 0 16 0 0 0 syscon auxv AT_CANARYLEN 0 0 17 0 0 0 syscon auxv AT_NCPUS 0 0 19 0 0 0 diff --git a/libc/sysv/consts/AT_MINSIGSTKSZ.S b/libc/sysv/consts/AT_MINSIGSTKSZ.S new file mode 100644 index 000000000..15758208f --- /dev/null +++ b/libc/sysv/consts/AT_MINSIGSTKSZ.S @@ -0,0 +1,2 @@ +#include "libc/sysv/consts/syscon.internal.h" +.syscon auxv,AT_MINSIGSTKSZ,51,0,0,0,0,0 diff --git a/libc/sysv/consts/auxv.h b/libc/sysv/consts/auxv.h index fb18fb88c..283904b23 100644 --- a/libc/sysv/consts/auxv.h +++ b/libc/sysv/consts/auxv.h @@ -6,21 +6,29 @@ COSMOPOLITAN_C_START_ extern const long AT_BASE; extern const long AT_BASE_PLATFORM; +extern const long AT_CANARY; +extern const long AT_CANARYLEN; extern const long AT_CLKTCK; extern const long AT_DCACHEBSIZE; extern const long AT_EGID; +extern const long AT_EHDRFLAGS; extern const long AT_ENTRY; extern const long AT_EUID; extern const long AT_EXECFD; extern const long AT_EXECFN; +extern const long AT_EXECPATH; extern const long AT_FLAGS; extern const long AT_GID; extern const long AT_HWCAP2; extern const long AT_HWCAP; extern const long AT_ICACHEBSIZE; +extern const long AT_MINSIGSTKSZ; +extern const long AT_NCPUS; extern const long AT_NOTELF; extern const long AT_NO_AUTOMOUNT; extern const long AT_OSRELDATE; +extern const long AT_PAGESIZES; +extern const long AT_PAGESIZESLEN; extern const long AT_PAGESZ; extern const long AT_PHDR; extern const long AT_PHENT; @@ -28,40 +36,41 @@ extern const long AT_PHNUM; extern const long AT_PLATFORM; extern const long AT_RANDOM; extern const long AT_SECURE; +extern const long AT_STACKBASE; +extern const long AT_STACKPROT; extern const long AT_SYSINFO_EHDR; +extern const long AT_TIMEKEEP; extern const long AT_UCACHEBSIZE; extern const long AT_UID; -extern const long AT_STACKBASE; -extern const long AT_EXECPATH; -extern const long AT_CANARY; -extern const long AT_CANARYLEN; -extern const long AT_NCPUS; -extern const long AT_PAGESIZES; -extern const long AT_PAGESIZESLEN; -extern const long AT_TIMEKEEP; -extern const long AT_STACKPROT; -extern const long AT_EHDRFLAGS; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #define AT_BASE SYMBOLIC(AT_BASE) #define AT_BASE_PLATFORM SYMBOLIC(AT_BASE_PLATFORM) +#define AT_CANARY SYMBOLIC(AT_CANARY) +#define AT_CANARYLEN SYMBOLIC(AT_CANARYLEN) #define AT_CLKTCK SYMBOLIC(AT_CLKTCK) #define AT_DCACHEBSIZE SYMBOLIC(AT_DCACHEBSIZE) #define AT_EGID SYMBOLIC(AT_EGID) +#define AT_EHDRFLAGS SYMBOLIC(AT_EHDRFLAGS) #define AT_ENTRY SYMBOLIC(AT_ENTRY) #define AT_EUID SYMBOLIC(AT_EUID) #define AT_EXECFD SYMBOLIC(AT_EXECFD) #define AT_EXECFN SYMBOLIC(AT_EXECFN) +#define AT_EXECPATH SYMBOLIC(AT_EXECPATH) #define AT_FLAGS SYMBOLIC(AT_FLAGS) #define AT_GID SYMBOLIC(AT_GID) #define AT_HWCAP SYMBOLIC(AT_HWCAP) #define AT_HWCAP2 SYMBOLIC(AT_HWCAP2) #define AT_ICACHEBSIZE SYMBOLIC(AT_ICACHEBSIZE) +#define AT_MINSIGSTKSZ SYMBOLIC(AT_MINSIGSTKSZ) +#define AT_NCPUS SYMBOLIC(AT_NCPUS) #define AT_NOTELF SYMBOLIC(AT_NOTELF) #define AT_NO_AUTOMOUNT SYMBOLIC(AT_NO_AUTOMOUNT) #define AT_OSRELDATE SYMBOLIC(AT_OSRELDATE) +#define AT_PAGESIZES SYMBOLIC(AT_PAGESIZES) +#define AT_PAGESIZESLEN SYMBOLIC(AT_PAGESIZESLEN) #define AT_PAGESZ SYMBOLIC(AT_PAGESZ) #define AT_PHDR SYMBOLIC(AT_PHDR) #define AT_PHENT SYMBOLIC(AT_PHENT) @@ -69,18 +78,11 @@ COSMOPOLITAN_C_END_ #define AT_PLATFORM SYMBOLIC(AT_PLATFORM) #define AT_RANDOM SYMBOLIC(AT_RANDOM) #define AT_SECURE SYMBOLIC(AT_SECURE) +#define AT_STACKBASE SYMBOLIC(AT_STACKBASE) +#define AT_STACKPROT SYMBOLIC(AT_STACKPROT) #define AT_SYSINFO_EHDR SYMBOLIC(AT_SYSINFO_EHDR) +#define AT_TIMEKEEP SYMBOLIC(AT_TIMEKEEP) #define AT_UCACHEBSIZE SYMBOLIC(AT_UCACHEBSIZE) #define AT_UID SYMBOLIC(AT_UID) -#define AT_STACKBASE SYMBOLIC(AT_STACKBASE) -#define AT_EXECPATH SYMBOLIC(AT_EXECPATH) -#define AT_CANARY SYMBOLIC(AT_CANARY) -#define AT_CANARYLEN SYMBOLIC(AT_CANARYLEN) -#define AT_NCPUS SYMBOLIC(AT_NCPUS) -#define AT_PAGESIZES SYMBOLIC(AT_PAGESIZES) -#define AT_PAGESIZESLEN SYMBOLIC(AT_PAGESIZESLEN) -#define AT_TIMEKEEP SYMBOLIC(AT_TIMEKEEP) -#define AT_STACKPROT SYMBOLIC(AT_STACKPROT) -#define AT_EHDRFLAGS SYMBOLIC(AT_EHDRFLAGS) #endif /* COSMOPOLITAN_LIBC_CALLS_AUXV_H_ */ diff --git a/test/libc/calls/printargs_test.c b/test/libc/calls/printargs_test.c index f447d2bf7..61e87389a 100644 --- a/test/libc/calls/printargs_test.c +++ b/test/libc/calls/printargs_test.c @@ -23,7 +23,7 @@ #include "libc/x/x.h" __attribute__((__constructor__)) static void init(void) { - pledge("stdio rpath tty", 0); + pledge("stdio rpath tty proc", 0); errno = 0; } diff --git a/test/libc/calls/sched_setscheduler_test.c b/test/libc/calls/sched_setscheduler_test.c index a1b2f4b8e..dfbb72d5c 100644 --- a/test/libc/calls/sched_setscheduler_test.c +++ b/test/libc/calls/sched_setscheduler_test.c @@ -22,6 +22,7 @@ #include "libc/dce.h" #include "libc/errno.h" #include "libc/intrin/kprintf.h" +#include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/sysv/consts/sched.h" #include "libc/testlib/testlib.h" @@ -53,6 +54,10 @@ bool CanTuneRealtimeSchedulers(void) { } } +TEST(sched_getscheduler, einval) { + ASSERT_SYS(IsLinux() ? EINVAL : ESRCH, -1, sched_getscheduler(INT_MIN)); +} + TEST(sched_setscheduler, test) { struct sched_param p = {sched_get_priority_min(SCHED_OTHER)}; EXPECT_SYS(0, DEFAULT_POLICY, sched_setscheduler(0, SCHED_OTHER, &p)); diff --git a/test/libc/mem/pledge_test.c b/test/libc/mem/pledge_test.c index c18ec356e..16e4167f1 100644 --- a/test/libc/mem/pledge_test.c +++ b/test/libc/mem/pledge_test.c @@ -372,7 +372,6 @@ TEST(pledge, chmod_ignoresDangerBits) { TEST(pledge, open_rpath) { if (IsOpenbsd()) return; // b/c testing linux bpf int ws, pid; - struct stat st; ASSERT_SYS(0, 0, touch("foo", 0644)); ASSERT_NE(-1, (pid = fork())); if (!pid) { @@ -389,7 +388,6 @@ TEST(pledge, open_rpath) { TEST(pledge, open_wpath) { if (IsOpenbsd()) return; // b/c testing linux bpf int ws, pid; - struct stat st; ASSERT_SYS(0, 0, touch("foo", 0644)); ASSERT_NE(-1, (pid = fork())); if (!pid) { @@ -426,7 +424,6 @@ TEST(pledge, open_cpath) { TEST(pledge, sigaction_isFineButForbidsSigsys) { if (IsOpenbsd()) return; // b/c testing linux bpf int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio", 0)); @@ -442,7 +439,6 @@ TEST(pledge, sigaction_isFineButForbidsSigsys) { TEST(pledge, execpromises_ok) { if (IsOpenbsd()) return; // b/c testing linux bpf int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio exec", "stdio")); @@ -457,7 +453,6 @@ TEST(pledge, execpromises_ok) { TEST(pledge, execpromises_notok) { if (IsOpenbsd()) return; // b/c testing linux bpf int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio exec", "stdio")); @@ -472,7 +467,6 @@ TEST(pledge, execpromises_notok) { TEST(pledge, execpromises_reducesAtExecOnLinux) { if (IsOpenbsd()) return; // b/c testing linux bpf int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio inet tty exec", "stdio tty")); @@ -487,7 +481,6 @@ TEST(pledge, execpromises_reducesAtExecOnLinux) { TEST(pledge_openbsd, execpromisesIsNull_letsItDoAnything) { if (!IsOpenbsd()) return; int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio exec", 0)); @@ -502,7 +495,6 @@ TEST(pledge_openbsd, execpromisesIsNull_letsItDoAnything) { TEST(pledge_openbsd, execpromisesIsSuperset_letsItDoAnything) { if (!IsOpenbsd()) return; int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio rpath exec", "stdio rpath tty inet")); @@ -522,7 +514,6 @@ TEST(pledge_linux, execpromisesIsSuperset_notPossible) { TEST(pledge_openbsd, execpromises_notok) { if (!IsOpenbsd()) return; int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio exec", "stdio")); @@ -537,7 +528,6 @@ TEST(pledge_openbsd, execpromises_notok) { TEST(pledge_openbsd, bigSyscalls) { if (IsOpenbsd()) return; // testing lunix int ws, pid; - struct stat st; ASSERT_NE(-1, (pid = fork())); if (!pid) { ASSERT_SYS(0, 0, pledge("stdio", 0)); @@ -572,6 +562,25 @@ TEST(pledge, threadWithLocks_canCodeMorph) { EXPECT_EQ(0, WEXITSTATUS(ws)); } +TEST(pledge, execWithoutRpath) { + int ws, pid; + ASSERT_SYS(0, 0, touch("foo", 0644)); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + ASSERT_SYS(0, 0, pledge("stdio prot_exec exec", "stdio prot_exec exec")); + ASSERT_SYS(EPERM, -1, open("foo", O_RDONLY)); + _Exit(0); + } + EXPECT_NE(-1, wait(&ws)); + if (IsOpenbsd()) { + EXPECT_TRUE(WIFSIGNALED(ws)); + EXPECT_EQ(SIGABRT, WTERMSIG(ws)); + } else { + EXPECT_TRUE(WIFEXITED(ws)); + EXPECT_EQ(0, WEXITSTATUS(ws)); + } +} + BENCH(pledge, bench) { int pid; if (!fork()) { diff --git a/test/tool/build/pledge_test.sh b/test/tool/build/pledge_test.sh new file mode 100755 index 000000000..6dcb5446c --- /dev/null +++ b/test/tool/build/pledge_test.sh @@ -0,0 +1,175 @@ +#!/bin/sh +m=tinylinux +t=/tmp/pledge-test + +if [ $# = 0 ]; then + if ! [ $(id -u) = 0 ]; then + make -j16 MODE= \ + o//examples/ls.com \ + o//examples/curl.com \ + o//examples/life.com \ + o//examples/hello.com \ + o//examples/printargs.com \ + o//tool/build/pledge.com || exit + make -j16 MODE=$m \ + o/$m/examples/ls.com \ + o/$m/examples/curl.com \ + o/$m/examples/life.com \ + o/$m/examples/hello.com \ + o/$m/examples/printargs.com \ + o/$m/tool/build/pledge.com || exit + test/tool/build/pledge_test.sh ape_binfmt_test_suite || exit + test/tool/build/pledge_test.sh ape_loader_test_suite || exit + test/tool/build/pledge_test.sh ape_assimilated_test_suite || exit + test/tool/build/pledge_test.sh ape_native_test_suite || exit + sudo test/tool/build/pledge_test.sh setuid_setup || exit + test/tool/build/pledge_test.sh setuid_test_suite || exit + else + echo need to run as an unprivileged user with sudo access >&2 + exit 1 + fi +fi + +check() { + if [ $? = 0 ]; then + printf '\e[32mok\e[0m\n' + else + echo failed >&2 + exit 1 + fi +} + +startit() { + printf 'testing %-30s ' "$*" >&2 +} + +checkem() { + if [ $? = 0 ]; then + printf '\e[1;32mOK\e[0m\n' + else + printf '\e[1;31mFAILED\e[0m\n' + exit 1 + fi +} + +if [ "$1" = setuid_setup ]; then + + rm -rf $t || exit + mkdir -p $t || exit + chmod 01777 $t || exit + cp o/$m/tool/build/pledge.com $t || exit + chmod 06755 $t/pledge.com || exit + +elif [ "$1" = ape_binfmt_test_suite ]; then + + ape/apeinstall.sh >/dev/null 2>&1 + + startit ape binfmt life.com + o//tool/build/pledge.com -p 'stdio rpath prot_exec' o//examples/life.com + [ $? = 42 ] + checkem + + startit ape binfmt hello.com + [ "$(o//tool/build/pledge.com -p 'stdio rpath prot_exec' o//examples/hello.com)" = "hello world" ] + checkem + + startit ape binfmt curl.com + [ "$(o//tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o//examples/curl.com https://justine.lol/hello.txt)" = "hello world" ] + checkem + +elif [ "$1" = ape_loader_test_suite ]; then + + ape/apeuninstall.sh >/dev/null 2>&1 + + startit ape loader life.com + o//tool/build/pledge.com -p 'stdio rpath prot_exec' o//examples/life.com + [ $? = 42 ] + checkem + + startit ape loader hello.com + [ "$(o//tool/build/pledge.com -p 'stdio rpath prot_exec' o//examples/hello.com)" = "hello world" ] + checkem + + startit ape loader curl.com + [ "$(o//tool/build/pledge.com -p 'stdio inet dns rpath prot_exec' o//examples/curl.com https://justine.lol/hello.txt)" = "hello world" ] + checkem + + ape/apeinstall.sh >/dev/null 2>&1 + +elif [ "$1" = ape_assimilated_test_suite ]; then + + mkdir -p $t/assimilated + + startit ape assimilated life.com + cp o//examples/life.com $t/assimilated + o//tool/build/assimilate.com $t/assimilated/life.com + o/$m/tool/build/pledge.com -p 'stdio' $t/assimilated/life.com + [ $? = 42 ] + checkem + + startit ape assimilated hello.com + cp o//examples/hello.com $t/assimilated + o//tool/build/assimilate.com $t/assimilated/hello.com + [ "$(o/$m/tool/build/pledge.com -p 'stdio' $t/assimilated/hello.com)" = "hello world" ] + checkem + + startit ape assimilated curl.com + cp o//examples/curl.com $t/assimilated + o//tool/build/assimilate.com $t/assimilated/curl.com + [ "$(o/$m/tool/build/pledge.com -p 'stdio inet dns' $t/assimilated/curl.com https://justine.lol/hello.txt)" = "hello world" ] + checkem + +elif [ "$1" = ape_native_test_suite ]; then + + startit ape native life.com + o/$m/tool/build/pledge.com -p 'stdio' o/$m/examples/life.com + [ $? = 42 ] + checkem + + startit ape native hello.com + [ "$(o/$m/tool/build/pledge.com -p 'stdio' o/$m/examples/hello.com)" = "hello world" ] + checkem + + startit ape native curl.com + [ "$(o/$m/tool/build/pledge.com -p 'stdio inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ] + checkem + +elif [ "$1" = setuid_test_suite ]; then + + startit setuid life.com + $t/pledge.com -p 'stdio' o/$m/examples/life.com + [ $? = 42 ] + checkem + + startit setuid hello.com + [ "$($t/pledge.com -p 'stdio' o/$m/examples/hello.com)" = "hello world" ] + checkem + + startit setuid curl.com + [ "$($t/pledge.com -p 'stdio inet dns' o/$m/examples/curl.com https://justine.lol/hello.txt)" = "hello world" ] + checkem + + startit setuid getuid + [ "$($t/pledge.com -pstdio o/$m/examples/printargs.com 2>&1 | grep getuid | grep -o [[:digit:]]*)" = "$(id -u)" ] + checkem + + startit setuid geteuid + [ "$($t/pledge.com -pstdio o/$m/examples/printargs.com 2>&1 | grep geteuid | grep -o [[:digit:]]*)" = "$(id -u)" ] + checkem + + startit setuid no capabilities + [ "$($t/pledge.com -pstdio o/$m/examples/printargs.com 2>&1 | grep CAP_ | wc -l)" = 0 ] + checkem + + startit setuid maximum nice + $t/pledge.com -np 'stdio proc' o/$m/examples/printargs.com 2>&1 | grep SCHED_IDLE >/dev/null + checkem + + startit setuid chroot + mkdir $t/jail && + touch $t/jail/hi && + cp o/$m/examples/ls.com $t/jail && + $t/pledge.com -v / -c $t/jail -p 'stdio rpath' /ls.com / | grep 'DT_REG /hi' >/dev/null + checkem + +fi diff --git a/tool/build/pledge.c b/tool/build/pledge.c index f78e89ada..887b74af9 100644 --- a/tool/build/pledge.c +++ b/tool/build/pledge.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/bits/safemacros.internal.h" #include "libc/calls/calls.h" #include "libc/calls/landlock.h" #include "libc/calls/struct/rlimit.h" @@ -56,6 +57,11 @@ #include "libc/x/x.h" #include "third_party/getopt/getopt.h" +// MANUALLY TESTED BY RUNNING +// +// test/tool/build/pledge_test.sh +// + STATIC_YOINK("strerror_wr"); #define USAGE \ @@ -94,7 +100,7 @@ usage: pledge.com [-hnN] PROG ARGS...\n\ - vminfo: allows /proc/stat, /proc/self/maps, etc.\n\ - tmppath: allows /tmp, $TMPPATH, lstat, unlink\n\ \n\ -pledge.com v1.1\n\ +pledge.com v1.2\n\ copyright 2022 justine alexandra roberts tunney\n\ https://twitter.com/justinetunney\n\ https://linkedin.com/in/jtunney\n\ @@ -352,19 +358,20 @@ void Unveil(const char *path, const char *perm) { } } -void UnveilIfExists(const char *path, const char *perm) { +int UnveilIfExists(const char *path, const char *perm) { int err; if (path) { err = errno; - if (unveil(path, perm) == -1) { - if (errno == ENOENT) { - errno = err; - } else { - kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm); - _Exit(20); - } + if (unveil(path, perm) != -1) { + return 0; + } else if (errno == ENOENT) { + errno = err; + } else { + kprintf("error: unveil(%#s, %#s) failed: %m\n", path, perm); + _Exit(20); } } + return -1; } void MakeProcessNice(void) { @@ -386,6 +393,7 @@ void MakeProcessNice(void) { } void ApplyFilesystemPolicy(unsigned long ipromises) { + const char *p; if (!SupportsLandlock()) { if (unveils.n) { @@ -460,7 +468,12 @@ void ApplyFilesystemPolicy(unsigned long ipromises) { } if (~ipromises & (1ul << PROMISE_PROT_EXEC)) { - UnveilIfExists("/usr/bin/ape", "rx"); + if (UnveilIfExists("/usr/bin/ape", "rx") == -1) { + UnveilIfExists(xjoinpaths(firstnonnull(getenv("TMPDIR"), + firstnonnull(getenv("HOME"), ".")), + ".ape"), + "rx"); + } } if (~ipromises & (1ul << PROMISE_VMINFO)) { @@ -552,15 +565,6 @@ int main(int argc, char *argv[]) { owneruid = geteuid(); hasfunbits = usergid != ownergid || useruid != owneruid; - if (g_dontdrop) { - if (hasfunbits) { - kprintf("error: -D flag forbidden on setuid binaries\n"); - _Exit(6); - } - } else { - DropCapabilities(); - } - if (hasfunbits) { setuid(owneruid); setgid(ownergid); @@ -575,7 +579,7 @@ int main(int argc, char *argv[]) { } // check if user has permission to chroot directory - if (hasfunbits) { + if (hasfunbits && g_chroot) { oldfsuid = setfsuid(useruid); oldfsgid = setfsgid(usergid); if (access(g_chroot, R_OK) == -1) { @@ -612,6 +616,15 @@ int main(int argc, char *argv[]) { setfsgid(oldfsgid); } + if (g_dontdrop) { + if (hasfunbits) { + kprintf("error: -D flag forbidden on setuid binaries\n"); + _Exit(6); + } + } else { + DropCapabilities(); + } + // set group id if (usergid != ownergid) { // setgid binaries must use the gid of the user that ran it diff --git a/tool/net/help.txt b/tool/net/help.txt index e5f073d8e..7d99d3b25 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -3930,12 +3930,13 @@ UNIX MODULE exec - Allows execve, access, faccessat, openat(O_RDONLY). + Allows execve. - If the executable in question needs a loader, then you may need - "prot_exec" too. With APE, security will be stronger if you + If the executable in question needs a loader, then you will need + "rpath prot_exec" too. With APE, security is strongest when you assimilate your binaries beforehand, using the --assimilate flag, - or the o//tool/build/assimilate.com program. + or the o//tool/build/assimilate.com program. On OpenBSD this is + mandatory. prot_exec