mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-05-28 08:12:28 +00:00
Rewrite Linux pledge() code so it can be a payload
It's now possible to build our pledge() polyfill as a dynamic shared object that can be injected into a glibc executable using LD_PRELOAD
This commit is contained in:
parent
7bd4179b9b
commit
0277d7d6e9
37 changed files with 1980 additions and 1600 deletions
|
@ -123,6 +123,7 @@ int killpg(int, int);
|
|||
int link(const char *, const char *) dontthrow;
|
||||
int linkat(int, const char *, int, const char *, int);
|
||||
int madvise(void *, uint64_t, int);
|
||||
int memfd_create(const char *, unsigned int);
|
||||
int mincore(void *, size_t, unsigned char *);
|
||||
int mkdir(const char *, uint32_t);
|
||||
int mkdirat(int, const char *, uint32_t);
|
||||
|
|
|
@ -188,11 +188,20 @@ o/$(MODE)/libc/calls/_timespec_frommicros.o: \
|
|||
OVERRIDE_CFLAGS += \
|
||||
-O2
|
||||
|
||||
o/$(MODE)/libc/calls/pledge.o \
|
||||
o/$(MODE)/libc/calls/pledge-linux.o \
|
||||
o/$(MODE)/libc/calls/unveil.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-DSTACK_FRAME_UNLIMITED
|
||||
|
||||
# we want -Os because:
|
||||
# it makes a big difference
|
||||
# we need pic because:
|
||||
# so it can be an LD_PRELOAD payload
|
||||
o/$(MODE)/libc/calls/pledge-linux.o: \
|
||||
OVERRIDE_CFLAGS += \
|
||||
-Os \
|
||||
-fPIC
|
||||
|
||||
LIBC_CALLS_LIBS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)))
|
||||
LIBC_CALLS_SRCS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_SRCS))
|
||||
LIBC_CALLS_HDRS = $(foreach x,$(LIBC_CALLS_ARTIFACTS),$($(x)_HDRS))
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "libc/bits/likely.h"
|
||||
#include "libc/bits/weaken.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/pledge.h"
|
||||
#include "libc/calls/pledge.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -30,8 +32,6 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
int sys_pledge_linux(unsigned long);
|
||||
|
||||
/**
|
||||
* Replaces current process with program.
|
||||
*
|
||||
|
@ -72,7 +72,7 @@ int execve(const char *prog, char *const argv[], char *const envp[]) {
|
|||
if (!IsWindows()) {
|
||||
rc = 0;
|
||||
if (IsLinux() && __execpromises && weaken(sys_pledge_linux)) {
|
||||
rc = weaken(sys_pledge_linux)(__execpromises);
|
||||
rc = weaken(sys_pledge_linux)(__execpromises, __pledge_mode, false);
|
||||
}
|
||||
if (!rc) {
|
||||
rc = sys_execve(prog, argv, envp);
|
||||
|
|
|
@ -136,6 +136,6 @@ char *GetProgramExecutableName(void) {
|
|||
return program_executable_name;
|
||||
}
|
||||
|
||||
const void *const GetProgramExecutableNameCtor[] initarray = {
|
||||
GetProgramExecutableName,
|
||||
};
|
||||
/* const void *const GetProgramExecutableNameCtor[] initarray = { */
|
||||
/* GetProgramExecutableName, */
|
||||
/* }; */
|
||||
|
|
35
libc/calls/memfd_create.c
Normal file
35
libc/calls/memfd_create.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*-*- 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/strace.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
||||
/**
|
||||
* Creates anonymous file.
|
||||
*
|
||||
* @param name is used for the `/proc/self/fd/FD` symlink
|
||||
* @param flags can have `MFD_CLOEXEC`, `MFD_ALLOW_SEALING`
|
||||
* @raise ENOSYS if not RHEL8+
|
||||
*/
|
||||
int memfd_create(const char *name, unsigned int flags) {
|
||||
int rc;
|
||||
rc = sys_memfd_create(name, flags);
|
||||
STRACE("memfd_create(%#s, %#x) → %d% m", name, flags, rc);
|
||||
return rc;
|
||||
}
|
66
libc/calls/parsepromises.c
Normal file
66
libc/calls/parsepromises.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*-*- 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/pledge.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
static int FindPromise(const char *name) {
|
||||
int i;
|
||||
for (i = 0; i < ARRAYLEN(kPledge); ++i) {
|
||||
if (!strcasecmp(name, kPledge[i].name)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the arguments to pledge() into a bitmask.
|
||||
*
|
||||
* @return 0 on success, or -1 if invalid
|
||||
*/
|
||||
int ParsePromises(const char *promises, unsigned long *out) {
|
||||
int rc = 0;
|
||||
int promise;
|
||||
unsigned long ipromises;
|
||||
char *tok, *state, *start, buf[256];
|
||||
if (promises) {
|
||||
ipromises = -1;
|
||||
if (memccpy(buf, promises, 0, sizeof(buf))) {
|
||||
start = buf;
|
||||
while ((tok = strtok_r(start, " \t\r\n", &state))) {
|
||||
if ((promise = FindPromise(tok)) != -1) {
|
||||
ipromises &= ~(1ULL << promise);
|
||||
} else {
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
start = 0;
|
||||
}
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
} else {
|
||||
ipromises = 0;
|
||||
}
|
||||
if (!rc) {
|
||||
*out = ipromises;
|
||||
}
|
||||
return rc;
|
||||
}
|
1625
libc/calls/pledge-linux.c
Normal file
1625
libc/calls/pledge-linux.c
Normal file
File diff suppressed because it is too large
Load diff
1568
libc/calls/pledge.c
1568
libc/calls/pledge.c
File diff suppressed because it is too large
Load diff
16
libc/calls/pledge.h
Normal file
16
libc/calls/pledge.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_PLEDGE_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_PLEDGE_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
enum PledgeMode {
|
||||
kPledgeModeKillThread,
|
||||
kPledgeModeKillProcess,
|
||||
kPledgeModeErrno,
|
||||
};
|
||||
|
||||
extern enum PledgeMode __pledge_mode;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_PLEDGE_H_ */
|
|
@ -1,9 +1,20 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_PLEDGE_INTERNAL_H_
|
||||
#include "libc/calls/pledge.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int ParsePromises(const char *, unsigned long *);
|
||||
struct Pledges {
|
||||
const char *name;
|
||||
const uint16_t *syscalls;
|
||||
const size_t len;
|
||||
};
|
||||
|
||||
hidden extern const struct Pledges kPledge[PROMISE_LEN_];
|
||||
|
||||
int sys_pledge_linux(unsigned long, enum PledgeMode, bool) hidden;
|
||||
int ParsePromises(const char *, unsigned long *) hidden;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -18,8 +18,10 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/getconsolectrlevent.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
|
@ -39,7 +41,7 @@ static textwindows inline bool HasWorkingConsole(void) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sends signal to this process.
|
||||
* Sends signal to this thread.
|
||||
*
|
||||
* @param sig can be SIGALRM, SIGINT, SIGTERM, SIGKILL, etc.
|
||||
* @return 0 on success or -1 w/ errno
|
||||
|
@ -56,8 +58,7 @@ int raise(int sig) {
|
|||
x = 1 / x;
|
||||
rc = 0;
|
||||
} else if (!IsWindows()) {
|
||||
// XXX: should be tkill() or tgkill() on linux
|
||||
rc = sys_kill(getpid(), sig, 1);
|
||||
rc = sys_tkill(gettid(), sig, 0);
|
||||
} else {
|
||||
if (HasWorkingConsole() && (event = GetConsoleCtrlEvent(sig)) != -1) {
|
||||
// XXX: MSDN says "If this parameter is zero, the signal is
|
||||
|
|
|
@ -290,9 +290,9 @@ int sys_unveil_linux(const char *path, const char *permissions) {
|
|||
* possible to use opendir() and go fishing for paths which weren't
|
||||
* previously known.
|
||||
*
|
||||
* 5. Use ftruncate() rather than truncate(). One of the backdoors with
|
||||
* Landlock is it currently can't restrict truncate() and setxattr()
|
||||
* which permits certain kinds of modifications to files outside the
|
||||
* 5. Use ftruncate() rather than truncate(). One issue Landlock hasn't
|
||||
* addressed yet is restrictions over truncate() and setxattr() which
|
||||
* could permit certain kinds of modifications to files outside the
|
||||
* sandbox. When your policy is committed, we install a SECCOMP BPF
|
||||
* filter to disable those calls, however similar trickery may be
|
||||
* possible through other unaddressed calls like ioctl(). Using the
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/nexgen32e/vendor.internal.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -39,11 +40,18 @@ privileged wontreturn void _Exit(int exitcode) {
|
|||
int i;
|
||||
STRACE("_Exit(%d)", exitcode);
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
asm volatile("syscall"
|
||||
: /* no outputs */
|
||||
: "a"(__NR_exit_group), "D"(exitcode)
|
||||
: "rcx", "r11", "memory");
|
||||
// this should only be possible on Linux in a pledge ultra sandbox
|
||||
// On Linux _Exit1 (exit) must be called in pledge("") mode. If we
|
||||
// call _Exit (exit_group) when we haven't used pledge("stdio") then
|
||||
// it'll terminate the process instead. On OpenBSD we must not call
|
||||
// _Exit1 (__threxit) because only _Exit (exit) is whitelisted when
|
||||
// operating in pledge("") mode.
|
||||
if (!(IsLinux() && !PLEDGED(STDIO))) {
|
||||
asm volatile("syscall"
|
||||
: /* no outputs */
|
||||
: "a"(__NR_exit_group), "D"(exitcode)
|
||||
: "rcx", "r11", "memory");
|
||||
}
|
||||
// Inline _Exit1() just in case _Exit() isn't allowed by pledge()
|
||||
asm volatile("syscall"
|
||||
: /* no outputs */
|
||||
: "a"(__NR_exit), "D"(exitcode)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
|
@ -35,6 +36,12 @@ privileged wontreturn void _Exit1(int rc) {
|
|||
struct WinThread *wt;
|
||||
STRACE("_Exit1(%d)", rc);
|
||||
if (!IsWindows() && !IsMetal()) {
|
||||
if (IsOpenbsd() && !PLEDGED(STDIO)) {
|
||||
asm volatile("syscall"
|
||||
: /* no outputs */
|
||||
: "a"(__NR_exit), "D"(rc)
|
||||
: "rcx", "r11", "memory");
|
||||
}
|
||||
asm volatile("xor\t%%r10d,%%r10d\n\t"
|
||||
"syscall"
|
||||
: /* no outputs */
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/pledge.h"
|
||||
#include "libc/intrin/promises.internal.h"
|
||||
|
||||
// XXX: should be inherited thread local
|
||||
unsigned __pledge_mode;
|
||||
// see also sys_pledge_linux() which is 100% pure
|
||||
enum PledgeMode __pledge_mode;
|
||||
unsigned long __promises;
|
||||
unsigned long __execpromises;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define PROMISE_PROT_EXEC 18
|
||||
#define PROMISE_VMINFO 19
|
||||
#define PROMISE_TMPPATH 20
|
||||
#define PROMISE_LEN_ 21
|
||||
|
||||
#define PLEDGED(x) ((~__promises >> PROMISE_##x) & 1)
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ 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 */
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall memfd_create,0xfffffffffffff13f,globl
|
2
libc/sysv/calls/sys_memfd_create.s
Normal file
2
libc/sysv/calls/sys_memfd_create.s
Normal file
|
@ -0,0 +1,2 @@
|
|||
.include "o/libc/sysv/macros.internal.inc"
|
||||
.scall sys_memfd_create,0xfffffffffffff13f,globl,hidden
|
|
@ -417,14 +417,6 @@ syscon at AT_REMOVEDIR 0x0200 0x80 0x0800 8 0x800 0x0200 # faked
|
|||
syscon at AT_EACCESS 0x0200 0x10 0x0100 1 0x100 0 # performs check using effective uid/gid; unnecessary nt
|
||||
syscon at AT_EMPTY_PATH 0x1000 0 0 0 0 0 # linux 2.6.39+; see unlink, O_TMPFILE, etc.
|
||||
|
||||
# memfd_create() flags
|
||||
#
|
||||
# Unsupported flags are encoded as 0.
|
||||
#
|
||||
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||
syscon memfd MFD_CLOEXEC 1 0 0 0 0 0
|
||||
syscon memfd MFD_ALLOW_SEALING 2 0 0 0 0 0
|
||||
|
||||
# utimensat() special values
|
||||
#
|
||||
# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon termios,CANBSIZ,255,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon misc,IPPORT_RESERVED,0x0400,0x0400,0x0400,0x0400,0x0400,0x0400
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon memfd,MFD_ALLOW_SEALING,2,0,0,0,0,0
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon memfd,MFD_CLOEXEC,1,0,0,0,0,0
|
|
@ -1,16 +1,7 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_MFD_H_
|
||||
#define COSMOPOLITAN_LIBC_SYSV_CONSTS_MFD_H_
|
||||
#include "libc/runtime/symbolic.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern const unsigned int MFD_CLOEXEC;
|
||||
extern const unsigned int MFD_ALLOW_SEALING;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
||||
#define MFD_CLOEXEC SYMBOLIC(MFD_CLOEXEC)
|
||||
#define MFD_ALLOW_SEALING SYMBOLIC(MFD_ALLOW_SEALING)
|
||||
#define MFD_CLOEXEC 1
|
||||
#define MFD_ALLOW_SEALING 2
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MFD_H_ */
|
||||
|
|
|
@ -350,7 +350,7 @@ scall sched_getattr 0xfffffffffffff13b globl # ├─ karen sandler requires s
|
|||
scall renameat2 0xfffffffffffff13c globl # └─ debian founder ian murdock found strangled with vacuum cord
|
||||
#scall seccomp 0xfffffffffffff13d globl # wrapped manually
|
||||
scall sys_getrandom 0xfff00723321f413e globl hidden # Linux 3.17+ and getentropy() on XNU/OpenBSD, coming to NetBSD in 9.2
|
||||
scall memfd_create 0xfffffffffffff13f globl # wut
|
||||
scall sys_memfd_create 0xfffffffffffff13f globl hidden
|
||||
scall kexec_file_load 0xfffffffffffff140 globl
|
||||
scall bpf 0xfffffffffffff141 globl
|
||||
scall execveat 0xfffffffffffff142 globl
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue