Improve pledge() usability and consistency

- We now kill the program on violations like OpenBSD
- We now print a message explaining which promise is needed
- This change also fixes a linkage bug with thread local storage
- Your sigaction() handlers should now be more thread safe

A new `__pledge_mode` global has been introduced to make pledge() more
customizable on Linux. For example:

    __attribute__((__constructor__)) static void init(void) {
      __pledge_mode = SECCOMP_RET_ERRNO | EPERM;
    }

Can be used to restore our old permissive pledge() behavior.
This commit is contained in:
Justine Tunney 2022-08-07 16:18:33 -07:00
parent 13c1c45075
commit 5546559034
30 changed files with 713 additions and 86 deletions

View file

@ -386,6 +386,7 @@ SECTIONS {
_tdata_start = .;
*(SORT_BY_ALIGNMENT(.tdata))
*(SORT_BY_ALIGNMENT(.tdata.*))
. = ALIGN(16);
_tdata_end = .;
. = ALIGN(PAGESIZE);
} :Tls
@ -398,6 +399,8 @@ SECTIONS {
_tbss_start = .;
*(SORT_BY_ALIGNMENT(.tbss))
*(SORT_BY_ALIGNMENT(.tbss.*))
. = ALIGN(16);
/* the %fs register is based on this location */
_tbss_end = .;
} :Tls

226
examples/statfs.c Normal file
View file

@ -0,0 +1,226 @@
#if 0
/*─────────────────────────────────────────────────────────────────╗
To the extent possible under law, Justine Tunney has waived
all copyright and related or neighboring rights to this file,
as it is written in the following disclaimers:
http://unlicense.org/ │
http://creativecommons.org/publicdomain/zero/1.0/ │
*/
#endif
#include "libc/calls/struct/statfs.h"
#include "libc/log/check.h"
#include "libc/stdio/stdio.h"
const char *DescribeStatfsType(int64_t x) {
switch (x) {
case 0xadf5:
return "ADFS_SUPER_MAGIC";
case 0xadff:
return "AFFS_SUPER_MAGIC";
case 0x0187:
return "AUTOFS_SUPER_MAGIC";
case 0x1373:
return "DEVFS_SUPER_MAGIC";
case 0x1cd1:
return "DEVPTS_SUPER_MAGIC";
case 0xf15f:
return "ECRYPTFS_SUPER_MAGIC";
case 0x137d:
return "EXT_SUPER_MAGIC";
case 0xef51:
return "EXT2_OLD_SUPER_MAGIC";
case 0xef53:
return "EXT2/3/4_SUPER_MAGIC";
case 0x4244:
return "HFS_SUPER_MAGIC";
case 0x9660:
return "ISOFS_SUPER_MAGIC";
case 0x72b6:
return "JFFS2_SUPER_MAGIC";
case 0x137f:
return "MINIX_SUPER_MAGIC";
case 0x138f:
return "MINIX_SUPER_MAGIC2";
case 0x2468:
return "MINIX2_SUPER_MAGIC";
case 0x2478:
return "MINIX2_SUPER_MAGIC2";
case 0x4d5a:
return "MINIX3_SUPER_MAGIC";
case 0x4d44:
return "MSDOS_SUPER_MAGIC";
case 0x564c:
return "NCP_SUPER_MAGIC";
case 0x6969:
return "NFS_SUPER_MAGIC";
case 0x3434:
return "NILFS_SUPER_MAGIC";
case 0x9fa1:
return "OPENPROM_SUPER_MAGIC";
case 0x9fa0:
return "PROC_SUPER_MAGIC";
case 0x002f:
return "QNX4_SUPER_MAGIC";
case 0x7275:
return "ROMFS_MAGIC";
case 0x517b:
return "SMB_SUPER_MAGIC";
case 0x9fa2:
return "USBDEVICE_SUPER_MAGIC";
case 0x27e0eb:
return "CGROUP_SUPER_MAGIC";
case 0xbad1dea:
return "FUTEXFS_SUPER_MAGIC";
case 0x5346414f:
return "AFS_SUPER_MAGIC";
case 0x09041934:
return "ANON_INODE_FS_MAGIC";
case 0x62646576:
return "BDEVFS_MAGIC";
case 0x42465331:
return "BEFS_SUPER_MAGIC";
case 0x1badface:
return "BFS_MAGIC";
case 0x42494e4d:
return "BINFMTFS_MAGIC";
case 0xcafe4a11:
return "BPF_FS_MAGIC";
case 0x9123683e:
return "BTRFS_SUPER_MAGIC";
case 0x73727279:
return "BTRFS_TEST_MAGIC";
case 0x63677270:
return "CGROUP2_SUPER_MAGIC";
case 0xff534d42:
return "CIFS_MAGIC_NUMBER";
case 0x73757245:
return "CODA_SUPER_MAGIC";
case 0x012ff7b7:
return "COH_SUPER_MAGIC";
case 0x28cd3d45:
return "CRAMFS_MAGIC";
case 0x64626720:
return "DEBUGFS_MAGIC";
case 0xde5e81e4:
return "EFIVARFS_MAGIC";
case 0x00414a53:
return "EFS_SUPER_MAGIC";
case 0xf2f52010:
return "F2FS_SUPER_MAGIC";
case 0x65735546:
return "FUSE_SUPER_MAGIC";
case 0x00c0ffee:
return "HOSTFS_SUPER_MAGIC";
case 0xf995e849:
return "HPFS_SUPER_MAGIC";
case 0x958458f6:
return "HUGETLBFS_MAGIC";
case 0x3153464a:
return "JFS_SUPER_MAGIC";
case 0x19800202:
return "MQUEUE_MAGIC";
case 0x11307854:
return "MTD_INODE_FS_MAGIC";
case 0x6e736673:
return "NSFS_MAGIC";
case 0x5346544e:
return "NTFS_SB_MAGIC";
case 0x7461636f:
return "OCFS2_SUPER_MAGIC";
case 0x794c7630:
return "OVERLAYFS_SUPER_MAGIC";
case 0x50495045:
return "PIPEFS_MAGIC";
case 0x6165676c:
return "PSTOREFS_MAGIC";
case 0x68191122:
return "QNX6_SUPER_MAGIC";
case 0x858458f6:
return "RAMFS_MAGIC";
case 0x52654973:
return "REISERFS_SUPER_MAGIC";
case 0x73636673:
return "SECURITYFS_MAGIC";
case 0xf97cff8c:
return "SELINUX_MAGIC";
case 0x43415d53:
return "SMACK_MAGIC";
case 0x534f434b:
return "SOCKFS_MAGIC";
case 0x73717368:
return "SQUASHFS_MAGIC";
case 0x62656572:
return "SYSFS_MAGIC";
case 0x012ff7b6:
return "SYSV2_SUPER_MAGIC";
case 0x012ff7b5:
return "SYSV4_SUPER_MAGIC";
case 0x01021994:
return "TMPFS_MAGIC";
case 0x74726163:
return "TRACEFS_MAGIC";
case 0x15013346:
return "UDF_SUPER_MAGIC";
case 0x00011954:
return "UFS_MAGIC";
case 0x01021997:
return "V9FS_MAGIC";
case 0xa501fcf5:
return "VXFS_SUPER_MAGIC";
case 0xabba1974:
return "XENFS_SUPER_MAGIC";
case 0x012ff7b4:
return "XENIX_SUPER_MAGIC";
case 0x58465342:
return "XFS_SUPER_MAGIC";
case 0x012fd16d:
return "_XIAFS_SUPER_MAGIC";
default:
break;
}
return "UNKNOWN";
}
void ShowIt(const char *path) {
struct statfs sf = {0};
CHECK_NE(-1, statfs(path, &sf));
printf("filesystem %s\n", path);
printf("f_type = %#x (%s)\n", sf.f_type, DescribeStatfsType(sf.f_type));
printf("f_bsize = %'zu (%s)\n", sf.f_bsize, "optimal transfer block size");
printf("f_blocks = %'zu (%s)\n", sf.f_blocks,
"total data blocks in filesystem");
printf("f_bfree = %'zu (%s)\n", sf.f_bfree, "free blocks in filesystem");
printf("f_bavail = %'zu (%s)\n", sf.f_bavail, "free blocks available to");
printf("f_files = %'zu (%s)\n", sf.f_files,
"total file nodes in filesystem");
printf("f_ffree = %'zu (%s)\n", sf.f_ffree,
"free file nodes in filesystem");
printf("f_fsid = %d:%d (%s)\n", (sf.f_fsid & 0xffff0000) >> 020,
(sf.f_fsid & 0x0000ffff) >> 000, "filesystem id");
printf("f_namelen = %'zu (%s)\n", sf.f_namelen,
"maximum length of filenames");
printf("f_frsize = %'zu (%s)\n", sf.f_frsize, "fragment size");
printf("f_flags = %#x", sf.f_flags);
if (sf.f_flags & 1) printf(" ST_RDONLY");
if (sf.f_flags & 2) printf(" ST_NOSUID");
if (sf.f_flags & 4) printf(" ST_NODEV");
if (sf.f_flags & 8) printf(" ST_NOEXEC");
if (sf.f_flags & 16) printf(" ST_SYNCHRONOUS");
if (sf.f_flags & 64) printf(" ST_MANDLOCK");
if (sf.f_flags & 128) printf(" ST_WRITE");
if (sf.f_flags & 256) printf(" ST_APPEND");
if (sf.f_flags & 512) printf(" ST_IMMUTABLE");
if (sf.f_flags & 1024) printf(" ST_NOATIME");
if (sf.f_flags & 2048) printf(" ST_NODIRATIME");
if (sf.f_flags & 4096) printf(" ST_RELATIME");
printf("\n");
printf("\n");
}
int main(int argc, char *argv[]) {
if (!IsLinux()) return 1;
ShowIt("/");
}

View file

@ -19,17 +19,5 @@
#include "libc/calls/state.internal.h"
#include "libc/intrin/pthread.h"
unsigned __sighandrvas[NSIG];
unsigned __sighandflags[NSIG];
static pthread_mutex_t __sig_lock_obj;
void(__sig_lock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__sig_lock_obj);
}
void(__sig_unlock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__sig_lock_obj);
}
_Thread_local unsigned __sighandrvas[NSIG];
_Thread_local unsigned __sighandflags[NSIG];

View file

@ -23,18 +23,27 @@
#include "libc/calls/struct/bpf.h"
#include "libc/calls/struct/filter.h"
#include "libc/calls/struct/seccomp.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/promises.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/nexgen32e/threaded.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/audit.h"
#include "libc/sysv/consts/kern.h"
#include "libc/sysv/consts/nrlinux.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/pr.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/sa.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/errfuns.h"
#define SPECIAL 0xf000
@ -63,6 +72,13 @@
#define PLEDGE(pledge) pledge, ARRAYLEN(pledge)
#define AbortPledge(reason) \
do { \
assert(!reason); \
asm("hlt"); \
unreachable; \
} while (0)
struct Filter {
size_t n;
struct sock_filter p[700];
@ -79,6 +95,7 @@ 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_sigreturn, //
__NR_linux_exit_group, //
__NR_linux_sched_yield, //
__NR_linux_sched_getaffinity, //
@ -175,7 +192,6 @@ static const uint16_t kPledgeLinuxStdio[] = {
__NR_linux_sigaltstack, //
__NR_linux_sigprocmask, //
__NR_linux_sigsuspend, //
__NR_linux_sigreturn, //
__NR_linux_sigpending, //
__NR_linux_socketpair, //
__NR_linux_getrusage, //
@ -432,7 +448,7 @@ static const struct sock_filter kFilterStart[] = {
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
// each filter assumes ordinal is already loaded into accumulator
BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)),
// Forbid some system calls with ENOSYS (rather than EPERM)
// forbid some system calls with ENOSYS (rather than EPERM)
BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, __NR_linux_memfd_secret, 5, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_rseq, 4, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_memfd_create, 3, 0),
@ -442,15 +458,91 @@ static const struct sock_filter kFilterStart[] = {
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (38 & SECCOMP_RET_DATA)),
};
static const struct sock_filter kFilterEnd[] = {
// if syscall isn't whitelisted then have it return -EPERM (-1)
static const struct sock_filter kFilterIgnoreExitGroup[] = {
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_exit_group, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | (1 & SECCOMP_RET_DATA)),
};
static void Log(const char *s, ...) {
va_list va;
va_start(va, s);
do {
write(2, s, strlen(s));
} while ((s = va_arg(va, const char *)));
va_end(va);
}
static bool HasSyscall(struct Pledges *p, uint16_t n) {
int i;
for (i = 0; i < p->len; ++i) {
if ((p->syscalls[i] & 0x0fff) == n) {
return true;
}
}
return false;
}
static char *FixCpy(char p[17], uint64_t x, uint8_t k) {
while (k > 0) *p++ = "0123456789abcdef"[(x >> (k -= 4)) & 15];
*p = '\0';
return p;
}
static char *HexCpy(char p[17], uint64_t x) {
return FixCpy(p, x, ROUNDUP(x ? bsrl(x) + 1 : 1, 4));
}
static void OnSigSys(int sig, siginfo_t *si, ucontext_t *ctx) {
int i;
bool found;
char ord[17], rip[17];
struct sigaction dfl = {.sa_sigaction = SIG_DFL};
ctx->uc_mcontext.rax = -si->si_errno;
FixCpy(ord, si->si_syscall, 12);
HexCpy(rip, ctx->uc_mcontext.rip);
for (found = i = 0; i < ARRAYLEN(kPledgeLinux); ++i) {
if (HasSyscall(kPledgeLinux + i, si->si_syscall)) {
Log("error: has not pledged ", kPledgeLinux[i].name, //
" (ord=", ord, " rip=", rip, ")\n", 0);
found = true;
break;
}
}
if (!found) {
Log("error: unsupported syscall (ord=", ord, " rip=", rip, ")\n", 0);
}
switch (__pledge_mode) {
case SECCOMP_RET_KILL_PROCESS:
if (!sigaction(SIGABRT, &dfl, 0)) {
sys_kill(getpid(), SIGABRT, 1);
}
_Exit(128 + SIGABRT);
case SECCOMP_RET_KILL_THREAD:
if (!sigaction(SIGABRT, &dfl, 0)) {
sys_tgkill(getpid(), gettid(), SIGABRT);
}
_Exit1(128 + SIGABRT);
default:
break;
}
}
static void MonitorSigSys(void) {
static _Thread_local bool once;
if (once) return;
once = true;
struct sigaction sa = {
.sa_sigaction = OnSigSys,
.sa_flags = SA_SIGINFO | SA_RESTART,
};
if (sigaction(SIGSYS, &sa, 0)) {
AbortPledge("sigaction failed");
}
}
static void AppendFilter(struct Filter *f, struct sock_filter *p, size_t n) {
if (UNLIKELY(f->n + n > ARRAYLEN(f->p))) {
asm("hlt"); // need to increase array size
unreachable;
AbortPledge("need to increase array size");
}
memcpy(f->p + f->n, p, n * sizeof(*f->p));
f->n += n;
@ -1093,20 +1185,22 @@ static void AllowSocketUnix(struct Filter *f) {
// - PR_SET_SECCOMP (22)
// - PR_SET_NO_NEW_PRIVS (38)
// - PR_CAPBSET_READ (23)
// - PR_CAPBSET_DROP (24)
//
static void 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])),
/*L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 15, 5, 0),
/*L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 16, 4, 0),
/*L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 21, 3, 0),
/*L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 2, 0),
/*L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 23, 1, 0),
/*L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 38, 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 */
/* L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_prctl, 0, 11 - 1),
/* L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[0])),
/* L2*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 15, 6, 0),
/* L3*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 16, 5, 0),
/* L4*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 21, 4, 0),
/* L5*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 22, 3, 0),
/* L6*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 23, 2, 0),
/* L7*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 24, 1, 0),
/* L8*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 38, 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 */
};
AppendFilter(f, PLEDGE(fragment));
}
@ -1220,8 +1314,7 @@ static void AppendPledge(struct Filter *f, const uint16_t *p, size_t len) {
};
AppendFilter(f, PLEDGE(fragment));
} else {
asm("hlt"); // list of ordinals exceeds max displacement
unreachable;
AbortPledge("list of ordinals exceeds max displacement");
}
}
@ -1311,8 +1404,7 @@ static void AppendPledge(struct Filter *f, const uint16_t *p, size_t len) {
AllowPrlimitStdio(f);
break;
default:
asm("hlt"); // switch forgot to define a special ordinal
unreachable;
AbortPledge("switch forgot to define a special ordinal");
}
}
}
@ -1322,7 +1414,14 @@ int sys_pledge_linux(unsigned long ipromises) {
struct Filter f;
CheckLargeStackAllocation(&f, sizeof(f));
f.n = 0;
// set up the seccomp filter
AppendFilter(&f, PLEDGE(kFilterStart));
if (ipromises == -1) {
// if we're pledging empty string, then avoid triggering a sigsys
// when _Exit() gets called since we need to fallback to _Exit1()
AppendFilter(&f, PLEDGE(kFilterIgnoreExitGroup));
}
if (!(~ipromises & (1ul << PROMISE_EXEC))) {
AppendOriginVerification(&f);
}
@ -1332,7 +1431,30 @@ int sys_pledge_linux(unsigned long ipromises) {
AppendPledge(&f, kPledgeLinux[i].syscalls, kPledgeLinux[i].len);
}
}
AppendFilter(&f, PLEDGE(kFilterEnd));
// now determine the default seccomp action
// the __pledge_mode global could be set to
// - SECCOMP_RET_KILL
// - SECCOMP_RET_KILL_THREAD
// - SECCOMP_RET_KILL_PROCESS
// - SECCOMP_RET_ERRNO | EPERM
struct sock_filter filter[1] = {BPF_STMT(BPF_RET | BPF_K, 0)};
if (~ipromises & (1ul << PROMISE_EXEC)) {
// our sigsys error message handler can't be inherited across
// execve() boundaries so if you've pledged exec then that'll
// mean no error messages for you.
filter[0].k = __pledge_mode;
AppendFilter(&f, PLEDGE(filter));
} else {
// if we haven't pledged exec, then we can monitor SIGSYS
// and print a helpful error message when things do break
// the handler then decides what to do with __pledge_mode
MonitorSigSys();
filter[0].k = SECCOMP_RET_TRAP | EPERM;
AppendFilter(&f, PLEDGE(filter));
}
// register our seccomp filter with the kernel
if ((rc = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) != -1) {
struct sock_fprog sandbox = {.len = f.n, .filter = f.p};
rc = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &sandbox);
@ -1388,11 +1510,13 @@ int ParsePromises(const char *promises, unsigned long *out) {
*
* Pledging causes most system calls to become unavailable. Your system
* call policy is enforced by the kernel, which means it can propagate
* across execve() if permitted. This system call is supported on
* OpenBSD and Linux where it's polyfilled using SECCOMP BPF. The way it
* works on Linux is verboten system calls will raise EPERM whereas
* OpenBSD just kills the process while logging a helpful message to
* /var/log/messages explaining which promise category you needed.
* across execve() if permitted. Root is not required. This system call
* is supported on OpenBSD and Linux where it's polyfilled using SECCOMP
* BPF. The way it works on Linux is, if a forbidden system call is used
* then the kernel will will the process. On OpenBSD, a helpful message
* explaining which promise is needed should be emitted to your system
* log. On Linux, we log that to stderr with one exception: reporting is
* currently not possible if you pledge exec.
*
* Timing is everything with pledge. For example, if you're using
* threads, then you may want to enable them explicitly *before* calling
@ -1548,6 +1672,7 @@ int ParsePromises(const char *promises, unsigned long *out) {
* @raise ENOSYS if host os isn't Linux or OpenBSD
* @raise EINVAL if `execpromises` on Linux isn't a subset of `promises`
* @raise EINVAL if `promises` allows exec and `execpromises` is null
* @threadsafe
*/
int pledge(const char *promises, const char *execpromises) {
int rc;

View file

@ -99,7 +99,6 @@ static bool __sig_deliver(bool restartable, int sig, int si_code,
STRACE("delivering %G", sig);
// enter the signal
__sig_lock();
rva = __sighandrvas[sig];
flags = __sighandflags[sig];
if ((~flags & SA_NODEFER) || (flags & SA_RESETHAND)) {
@ -110,7 +109,6 @@ static bool __sig_deliver(bool restartable, int sig, int si_code,
// signal handler. in that case you must use SA_NODEFER.
__sighandrvas[sig] = (int32_t)(intptr_t)SIG_DFL;
}
__sig_unlock();
// setup the somewhat expensive information args
// only if they're requested by the user in sigaction()

View file

@ -37,7 +37,6 @@
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/spinlock.h"
#include "libc/limits.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/log.h"
@ -245,7 +244,7 @@ static int __sigaction(int sig, const struct sigaction *act,
}
/**
* Installs handler for kernel interrupt, e.g.:
* Installs handler for kernel interrupt to thread, e.g.:
*
* void GotCtrlC(int sig, siginfo_t *si, ucontext_t *ctx);
* struct sigaction sa = {.sa_sigaction = GotCtrlC,
@ -445,6 +444,7 @@ static int __sigaction(int sig, const struct sigaction *act,
* @return 0 on success or -1 w/ errno
* @see xsigaction() for a much better api
* @asyncsignalsafe
* @threadsafe
* @vforksafe
*/
int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) {
@ -452,9 +452,7 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) {
if (sig == SIGKILL || sig == SIGSTOP) {
rc = einval();
} else {
__sig_lock();
rc = __sigaction(sig, act, oldact);
__sig_unlock();
}
STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(0, act),
DescribeSigaction(rc, oldact), rc);

32
libc/calls/siglock.c Normal file
View file

@ -0,0 +1,32 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/state.internal.h"
#include "libc/intrin/pthread.h"
static pthread_mutex_t __sig_lock_obj;
void(__sig_lock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_lock(&__sig_lock_obj);
}
void(__sig_unlock)(void) {
__sig_lock_obj.attr = PTHREAD_MUTEX_RECURSIVE;
pthread_mutex_unlock(&__sig_lock_obj);
}

View file

@ -7,8 +7,8 @@ COSMOPOLITAN_C_START_
hidden extern int __vforked;
hidden extern bool __time_critical;
hidden extern unsigned __sighandrvas[NSIG];
hidden extern unsigned __sighandflags[NSIG];
hidden _Thread_local extern unsigned __sighandrvas[NSIG];
hidden _Thread_local extern unsigned __sighandflags[NSIG];
hidden extern const struct NtSecurityAttributes kNtIsInheritable;
void __fds_lock(void);

View file

@ -0,0 +1,26 @@
#ifndef COSMOPOLITAN_LIBC_CALLS_STRUCT_STATFS_H_
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_STATFS_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct statfs {
int64_t f_type; /* type of filesystem */
int64_t f_bsize; /* optimal transfer block size */
int64_t f_blocks; /* total data blocks in filesystem */
int64_t f_bfree; /* free blocks in filesystem */
int64_t f_bavail; /* free blocks available to */
int64_t f_files; /* total file nodes in filesystem */
int64_t f_ffree; /* free file nodes in filesystem */
int64_t f_fsid; /* filesystem id */
int64_t f_namelen; /* maximum length of filenames */
int64_t f_frsize; /* fragment size */
int64_t f_flags; /* mount flags of filesystem 2.6.36 */
int64_t f_spare[4];
};
int statfs(const char *, struct statfs *);
int fstatfs(int, struct statfs *);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_STATFS_H_ */

View file

@ -68,7 +68,6 @@ i32 sys_mincore(void *, u64, unsigned char *) hidden;
i32 sys_mkdirat(i32, const char *, u32) hidden;
i32 sys_mkfifo(const char *, u32) hidden;
i32 sys_mknod(const char *, u32, u64) hidden;
i32 sys_unmount(const char *, i32) hidden;
i32 sys_mprotect(void *, u64, i32) hidden;
i32 sys_msync(void *, u64, i32) hidden;
i32 sys_munmap(void *, u64) hidden;
@ -97,11 +96,13 @@ i32 sys_sigaltstack(const void *, void *) hidden;
i32 sys_symlinkat(const char *, i32, const char *) hidden;
i32 sys_sync(void) hidden;
i32 sys_sync_file_range(i32, i64, i64, u32) hidden;
i32 sys_syslog(i32, char *, i32) hidden;
i32 sys_tgkill(i32, i32, i32) hidden;
i32 sys_tkill(i32, i32, void *) hidden;
i32 sys_truncate(const char *, u64, u64) hidden;
i32 sys_uname(void *) hidden;
i32 sys_unlinkat(i32, const char *, i32) hidden;
i32 sys_unmount(const char *, i32) hidden;
i32 sys_unveil(const char *, const char *) hidden;
i64 sys_copy_file_range(i32, long *, i32, long *, u64, u32) hidden;
i64 sys_getrandom(void *, u64, u32) hidden;

View file

@ -19,5 +19,6 @@
#include "libc/intrin/promises.internal.h"
// XXX: should be inherited thread local
unsigned __pledge_mode;
unsigned long __promises;
unsigned long __execpromises;

View file

@ -158,6 +158,11 @@ const char *GetSiCodeName(int sig, int si_code) {
} else if (si_code == POLL_HUP) {
strcpy(b + 5, "HUP"); /* device disconnected */
}
} else if (sig == SIGSYS) {
NameIt(b, "SYS_", si_code);
if (si_code == SYS_SECCOMP) {
strcpy(b + 4, "SECCOMP");
}
}
return b;
}

View file

@ -90,13 +90,13 @@ __oncrash_sigbus:
.endfn __oncrash_sigbus,globl
.org 11*7
__oncrash_sigsys:
__oncrash_sigurg:
push %rbp
mov %rsp,%rbp
call __oncrash
pop %rbp
ret
.endfn __oncrash_sigsys,globl
.endfn __oncrash_sigurg,globl
// </SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c, internal.h

View file

@ -111,7 +111,7 @@ void ShowCrashReports(void) {
kCrashSigs[4] = SIGTRAP; /* bad system call */
kCrashSigs[5] = SIGABRT; /* abort() called */
kCrashSigs[6] = SIGBUS; /* misaligned, noncanonical ptr, etc. */
kCrashSigs[7] = SIGSYS; /* bad system call */
kCrashSigs[7] = SIGURG; /* placeholder */
/* </SYNC-LIST>: showcrashreports.c, oncrashthunks.S, oncrash.c */
if (!IsWindows()) {
bzero(&ss, sizeof(ss));

View file

@ -55,10 +55,6 @@
STATIC_YOINK("_check_sigchld");
extern int64_t __wincrashearly;
extern unsigned char __data_start[]; /* αpε */
extern unsigned char __data_end[]; /* αpε */
extern unsigned char __bss_start[]; /* αpε */
extern unsigned char __bss_end[]; /* αpε */
bool32 __onntconsoleevent_nt(uint32_t);
static textwindows wontreturn void AbortFork(const char *func) {

View file

@ -17,6 +17,7 @@ extern intptr_t __oldstack; /* CRT */
extern uint64_t __nosync; /* SYS */
extern _Atomic(int) __ftrace; /* SYS */
extern _Atomic(int) __strace; /* SYS */
extern uint32_t __pledge_mode; /* SYS */
extern char *program_invocation_name; /* RII */
extern char *program_invocation_short_name; /* RII */
extern uint64_t __syscount; /* RII */
@ -41,6 +42,10 @@ extern unsigned char *__relo_start[]; /* αpε */
extern unsigned char *__relo_end[]; /* αpε */
extern uint8_t __zip_start[]; /* αpε */
extern uint8_t __zip_end[]; /* αpε */
extern uint8_t __data_start[]; /* αpε */
extern uint8_t __data_end[]; /* αpε */
extern uint8_t __bss_start[]; /* αpε */
extern uint8_t __bss_end[]; /* αpε */
extern size_t __virtualmax;
extern bool __isworker;

View file

@ -0,0 +1,2 @@
.include "o/libc/sysv/macros.internal.inc"
.scall sys_syslog,0xfffffffffffff067,globl,hidden

View file

@ -1,2 +0,0 @@
.include "o/libc/sysv/macros.internal.inc"
.scall syslog,0xfffffffffffff067,globl

21
libc/sysv/consts/kern.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_KERN_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_KERN_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define KERN_SOH "\001"
#define KERN_SOH_ASCII '\001'
#define KERN_EMERG KERN_SOH "0"
#define KERN_ALERT KERN_SOH "1"
#define KERN_CRIT KERN_SOH "2"
#define KERN_ERR KERN_SOH "3"
#define KERN_WARNING KERN_SOH "4"
#define KERN_NOTICE KERN_SOH "5"
#define KERN_INFO KERN_SOH "6"
#define KERN_DEBUG KERN_SOH "7"
#define KERN_DEFAULT ""
#define KERN_CONT KERN_SOH "c"
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_KERN_H_ */

View file

@ -0,0 +1,19 @@
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_LOGLEVEL_H_
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_LOGLEVEL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define LOGLEVEL_SCHED -2
#define LOGLEVEL_DEFAULT -1
#define LOGLEVEL_EMERG 0
#define LOGLEVEL_ALERT 1
#define LOGLEVEL_CRIT 2
#define LOGLEVEL_ERR 3
#define LOGLEVEL_WARNING 4
#define LOGLEVEL_NOTICE 5
#define LOGLEVEL_INFO 6
#define LOGLEVEL_DEBUG 7
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_LOGLEVEL_H_ */

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/sysv/consts/nrlinux.h"
#include "libc/macros.internal.h"
.privileged
@ -25,8 +26,8 @@ __restore_bt:
.endfn __restore_bt,globl,hidden
nop # gap so that __get_symbol(st, addr - 1) fails
.align 16
__restore_rt: # @see gdb/amd64-linux-tdep.c
mov $0x000f,%rax # [sic]
__restore_rt: # @see gdb/amd64-linux-tdep.c
mov $__NR_linux_sigreturn,%rax # [sic]
syscall
.align 16
.endfn __restore_rt,globl,hidden

View file

@ -140,7 +140,7 @@ scall __sys_getrusage 0x1bd0130752075062 globl hidden
scall sys_sysinfo 0xfffffffffffff063 globl hidden
scall sys_times 0xfffffffffffff064 globl hidden
scall sys_ptrace 0x01a01a01a201a065 globl hidden
scall syslog 0xfffffffffffff067 globl
scall sys_syslog 0xfffffffffffff067 globl hidden
scall sys_getuid 0x0180180182018066 globl hidden
scall sys_getgid 0x02f02f02f202f068 globl hidden
scall sys_getppid 0xfff027027202706e globl hidden # see sys_getpid()→edx for netbsd

View file

@ -0,0 +1,134 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/seccomp.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/sock/sock.h"
#include "libc/sysv/consts/af.h"
#include "libc/sysv/consts/ipproto.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/sock.h"
#include "libc/testlib/testlib.h"
#define SPAWN(METHOD) \
{ \
int ws, pid; \
ASSERT_NE(-1, (pid = METHOD())); \
if (!pid) {
#define EXITS(rc) \
_Exit(0); \
} \
ASSERT_NE(-1, wait(&ws)); \
ASSERT_TRUE(WIFEXITED(ws)); \
ASSERT_EQ(rc, WEXITSTATUS(ws)); \
}
#define TERMS(sig) \
_Exit(0); \
} \
ASSERT_NE(-1, wait(&ws)); \
ASSERT_TRUE(WIFSIGNALED(ws)); \
ASSERT_EQ(sig, WTERMSIG(ws)); \
}
void SetUp(void) {
if (!__is_linux_2_6_23() && !IsOpenbsd()) exit(0);
}
TEST(pledge, testSoftError) {
if (IsOpenbsd()) return;
SPAWN(fork);
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
ASSERT_SYS(0, 0, pledge("stdio", 0));
ASSERT_SYS(EPERM, -1, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
_Exit(7);
EXITS(7);
}
TEST(pledge, testKillThreadMode) {
SPAWN(fork);
__pledge_mode = SECCOMP_RET_KILL_THREAD;
ASSERT_SYS(0, 0, pledge("stdio", 0));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
}
TEST(pledge, testKillProcessMode) {
SPAWN(fork);
__pledge_mode = SECCOMP_RET_KILL_PROCESS;
ASSERT_SYS(0, 0, pledge("stdio", 0));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
}
TEST(pledge, testLogMessage_onSoftyMode) {
if (IsOpenbsd()) return;
int fds[2];
char msg[64] = {0};
ASSERT_SYS(0, 0, pipe(fds));
SPAWN(fork);
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
ASSERT_SYS(0, 2, dup2(fds[1], 2));
ASSERT_SYS(0, 0, pledge("stdio", 0));
ASSERT_SYS(EPERM, -1, socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
EXITS(0);
close(fds[1]);
read(fds[0], msg, sizeof(msg));
close(fds[0]);
if (IsLinux()) {
ASSERT_STARTSWITH("error: has not pledged inet", msg);
}
}
TEST(pledge, testLogMessage_onKillProcess) {
int fds[2];
char msg[64] = {0};
ASSERT_SYS(0, 0, pipe(fds));
SPAWN(fork);
__pledge_mode = SECCOMP_RET_KILL;
ASSERT_SYS(0, 2, dup2(fds[1], 2));
ASSERT_SYS(0, 0, pledge("stdio", 0));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
close(fds[1]);
read(fds[0], msg, sizeof(msg));
close(fds[0]);
if (IsLinux()) {
ASSERT_STARTSWITH("error: has not pledged inet", msg);
}
}
TEST(pledge, testNoLogPossibleSadly_becausePledgedExec) {
int fds[2];
char msg[64] = {0};
ASSERT_SYS(0, 0, pipe(fds));
SPAWN(fork);
ASSERT_SYS(0, 2, dup2(fds[1], 2));
ASSERT_SYS(0, 0, pledge("stdio exec", "stdio exec"));
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
TERMS(IsOpenbsd() ? SIGABRT : SIGSYS);
close(fds[1]);
read(fds[0], msg, sizeof(msg));
close(fds[0]);
ASSERT_STREQ("", msg);
}

View file

@ -60,6 +60,10 @@ STATIC_YOINK("zip_uri_support");
char testlib_enable_tmp_setup_teardown;
__attribute__((__constructor__)) static void init(void) {
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
}
void OnSig(int sig) {
// do nothing
}
@ -108,6 +112,20 @@ TEST(pledge, default_allowsExit) {
EXPECT_SYS(0, 0, munmap(job, FRAMESIZE));
}
TEST(pledge, execpromises_notok) {
if (IsOpenbsd()) return; // b/c testing linux bpf
int ws, pid;
ASSERT_NE(-1, (pid = fork()));
if (!pid) {
ASSERT_SYS(0, 0, pledge("stdio rpath exec", "stdio"));
execl("sock.elf", "sock.elf", 0);
_Exit(127);
}
EXPECT_NE(-1, wait(&ws));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(129, WEXITSTATUS(ws));
}
int Enclave(void *arg, int tid) {
ASSERT_SYS(0, 0, pledge("", 0));
int *job = arg; // get job
@ -478,7 +496,7 @@ TEST(pledge, execpromises_ok) {
EXPECT_EQ(42, WEXITSTATUS(ws));
}
TEST(pledge, execpromises_notok) {
TEST(pledge, execpromises_notok1) {
if (IsOpenbsd()) return; // b/c testing linux bpf
int ws, pid;
ASSERT_NE(-1, (pid = fork()));

View file

@ -367,22 +367,13 @@ TEST(unveil, usedTwice_forbidden_worksWithPledge) {
ASSERT_SYS(EACCES_OR_ENOENT, -1, open("garden/secret.txt", O_RDONLY));
// verify the first filter is still working
*gotsome = true;
ASSERT_SYS(EPERM, -1, socket(AF_UNIX, SOCK_STREAM, 0));
if (IsLinux()) {
ASSERT_SYS(0, 0, stat("garden/secret.txt", &st));
ASSERT_EQ(5, st.st_size); // wut linux metadata is accessible
}
socket(AF_UNIX, SOCK_STREAM, 0);
_Exit(0);
}
ASSERT_NE(-1, wait(&ws));
ASSERT_TRUE(*gotsome);
if (IsOpenbsd()) {
ASSERT_TRUE(WIFSIGNALED(ws));
ASSERT_EQ(SIGABRT, WTERMSIG(ws));
} else {
ASSERT_TRUE(WIFEXITED(ws));
ASSERT_EQ(0, WEXITSTATUS(ws));
}
ASSERT_TRUE(WIFSIGNALED(ws));
ASSERT_EQ(IsOpenbsd() ? SIGABRT : SIGSYS, WTERMSIG(ws));
EXPECT_SYS(0, 0, munmap(gotsome, FRAMESIZE));
}

View file

@ -16,6 +16,9 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/testlib/testlib.h"
@ -23,6 +26,21 @@ _Thread_local int x;
_Thread_local int y = 40;
int z = 2;
void PrintInfo(void) {
kprintf("_tdata_size = %d\n", _tdata_size);
kprintf("_tls_size = %d\n", _tls_size);
kprintf("_tls_content = %d\n", _tls_content);
kprintf("__data_start = %p\n", __data_start);
kprintf("__data_end = %p\n", __data_end);
kprintf("_tdata_start = %p\n", _tdata_start);
kprintf("_tdata_end = %p\n", _tdata_end);
kprintf("_tbss_start = %p\n", _tbss_start);
kprintf("_tbss_end = %p\n", _tbss_end);
kprintf("&y = %p\n", &y);
kprintf("__bss_start = %p\n", __bss_start);
kprintf("__bss_end = %p\n", __bss_end);
}
TEST(tls, test) {
EXPECT_EQ(42, x + y + z);
}

View file

@ -16,6 +16,10 @@
gotsigusr1 = false
tmpdir = "o/tmp/lunix_test.%d" % {unix.getpid()}
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
function OnSigUsr1(sig)
gotsigusr1 = true
end
@ -67,15 +71,18 @@ function UnixTest()
-- 2. sandbox the process
-- 3. then violate its security
if GetHostOs() == "LINUX" then
reader, writer = assert(unix.pipe())
if assert(unix.fork()) == 0 then
assert(unix.dup(writer, 2))
assert(unix.pledge("stdio"))
_, err = unix.socket()
assert(err:errno() == unix.EPERM)
unix.socket()
unix.exit(0)
end
unix.close(writer)
unix.close(reader)
pid, ws = assert(unix.wait())
assert(unix.WIFEXITED(ws))
assert(unix.WEXITSTATUS(ws) == 0)
assert(unix.WIFSIGNALED(ws))
assert(unix.WTERMSIG(ws) == unix.SIGSYS)
elseif GetHostOs() == "OPENBSD" then
if assert(unix.fork()) == 0 then
assert(unix.pledge("stdio"))

View file

@ -1381,6 +1381,7 @@ static int LuaUnixSiocgifconf(lua_State *L) {
// └─→ nil, unix.Errno
static int LuaUnixPledge(lua_State *L) {
int olderr = errno;
__pledge_mode = 0;
return SysretBool(L, "pledge", olderr,
pledge(luaL_checkstring(L, 1), luaL_optstring(L, 2, 0)));
}

View file

@ -259,7 +259,7 @@ SECURITY
-S (online policy)
This causes unix.pledge("stdio rpath inet dns") to be called on
This causes unix.pledge("stdio rpath inet dns id") to be called on
workers after fork() is called. This permits read-only operations
and APIs like Fetch() that let workers send and receive data with
private and public Internet hosts. Access to the unix module is
@ -267,10 +267,12 @@ SECURITY
-SS (offline policy)
This causes unix.pledge("stdio rpath") to be called on workers
This causes unix.pledge("stdio rpath id") to be called on workers
after after fork() is called. This prevents workers from talking
to the network (other than the client) and allows read-only file
system access (e.g. `-D DIR` flag).
system access (e.g. `-D DIR` flag). The `id` group helps you to
call other functions important to redbean security, such as the
unix.setrlimit() function.
-SSS (contained policy)
@ -281,6 +283,11 @@ SECURITY
should only be able to serve from its own zip file in this mode.
Lua script access to the unix module is highly restricted.
Unlike the unix.pledge() function, these sandboxing flags use a more
permissive policy on Linux. Rather than killing the process, they'll
cause system calls to fail with EPERM instead. Therefore these flags
should be gentler when you want security errors to be recoverable.
See http://redbean.dev for further details.
────────────────────────────────────────────────────────────────────────────────
@ -3834,8 +3841,11 @@ UNIX MODULE
This can be used to sandbox your redbean workers. It allows finer
customization compared to the `-S` flag.
Pledging causes most system calls to become unavailable. On Linux the
disabled calls will return EPERM whereas OpenBSD kills the process.
Pledging causes most system calls to become unavailable. If a
forbidden system call is used, then the process will be killed. In
that case, on OpenBSD, your system log will explain which promise
you need. On Linux, we report the promise to stderr, with one
exception: reporting is currently not possible if you pledge exec.
Using pledge is irreversible. On Linux it causes PR_SET_NO_NEW_PRIVS
to be set on your process.

View file

@ -25,6 +25,7 @@
#include "libc/calls/struct/flock.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/rusage.h"
#include "libc/calls/struct/seccomp.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/termios.h"
@ -32,6 +33,7 @@
#include "libc/dns/dns.h"
#include "libc/dns/hoststxt.h"
#include "libc/dos.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/kprintf.h"
@ -6585,17 +6587,18 @@ static void UnveilRedbean(void) {
}
static int EnableSandbox(void) {
__pledge_mode = SECCOMP_RET_ERRNO | EPERM;
switch (sandboxed) {
case 0:
return 0;
case 1: // -S
DEBUGF("(stat) applying '%s' sandbox policy", "online");
UnveilRedbean();
return pledge("stdio rpath inet dns", 0);
return pledge("stdio rpath inet dns id", 0);
case 2: // -SS
DEBUGF("(stat) applying '%s' sandbox policy", "offline");
UnveilRedbean();
return pledge("stdio rpath", 0);
return pledge("stdio rpath id", 0);
default: // -SSS
DEBUGF("(stat) applying '%s' sandbox policy", "contained");
UnveilRedbean();