mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
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:
parent
13c1c45075
commit
5546559034
30 changed files with 713 additions and 86 deletions
|
@ -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
226
examples/statfs.c
Normal 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("/");
|
||||
}
|
|
@ -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];
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
32
libc/calls/siglock.c
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
|
|
26
libc/calls/struct/statfs.h
Normal file
26
libc/calls/struct/statfs.h
Normal 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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
2
libc/sysv/calls/sys_syslog.s
Normal file
2
libc/sysv/calls/sys_syslog.s
Normal file
|
@ -0,0 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_syslog,0xfffffffffffff067,globl,hidden
|
|
@ -1,2 +0,0 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall syslog,0xfffffffffffff067,globl
|
21
libc/sysv/consts/kern.h
Normal file
21
libc/sysv/consts/kern.h
Normal 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_ */
|
19
libc/sysv/consts/loglevel.h
Normal file
19
libc/sysv/consts/loglevel.h
Normal 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_ */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
134
test/libc/calls/pledge2_test.c
Normal file
134
test/libc/calls/pledge2_test.c
Normal 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);
|
||||
}
|
|
@ -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()));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
|
|
1
third_party/lua/lunix.c
vendored
1
third_party/lua/lunix.c
vendored
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue