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:
Justine Tunney 2022-08-08 11:41:08 -07:00
parent 7bd4179b9b
commit 0277d7d6e9
37 changed files with 1980 additions and 1600 deletions

View file

@ -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);

View file

@ -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))

View file

@ -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);

View file

@ -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
View 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;
}

View 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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

16
libc/calls/pledge.h Normal file
View 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_ */

View file

@ -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) */

View file

@ -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

View file

@ -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