Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loader path security #1012

Merged
merged 17 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 7 additions & 28 deletions ape/ape-m1.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
#include <unistd.h>

#define pagesz 16384
#define VARNAME "COSMOPOLITAN_PROGRAM_EXECUTABLE="
#define VARSIZE (sizeof(VARNAME) - 1)
/* maximum path size that cosmo can take */
#define PATHSIZE (PATH_MAX < 1024 ? PATH_MAX : 1024)
#define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24)
Expand Down Expand Up @@ -203,11 +201,8 @@ struct PathSearcher {
unsigned long namelen;
const char *name;
const char *syspath;
char varname[VARSIZE];
char path[PATHSIZE];
};
_Static_assert(offsetof(struct PathSearcher, varname) + VARSIZE ==
offsetof(struct PathSearcher, path), "struct layout");

struct ApeLoader {
struct PathSearcher ps;
Expand Down Expand Up @@ -563,7 +558,8 @@ static long sys_pselect(int nfds, fd_set *readfds, fd_set *writefds,
__attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
long *sp, struct ElfEhdr *e,
struct ElfPhdr *p,
struct Syslib *lib) {
struct Syslib *lib,
char *path) {
long rc;
int prot;
int flags;
Expand Down Expand Up @@ -734,10 +730,10 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
close(fd);

register long *x0 __asm__("x0") = sp;
register char *x2 __asm__("x2") = path;
register struct Syslib *x15 __asm__("x15") = lib;
register long x16 __asm__("x16") = e->e_entry;
__asm__ volatile("mov\tx1,#0\n\t"
"mov\tx2,#0\n\t"
"mov\tx3,#0\n\t"
"mov\tx4,#0\n\t"
"mov\tx5,#0\n\t"
Expand Down Expand Up @@ -767,7 +763,7 @@ __attribute__((__noreturn__)) static void Spawn(const char *exe, int fd,
"mov\tx0,#0\n\t"
"br\tx16"
: /* no outputs */
: "r"(x0), "r"(x15), "r"(x16)
: "r"(x0), "r"(x2), "r"(x15), "r"(x16)
: "memory");
__builtin_unreachable();
}
Expand Down Expand Up @@ -891,7 +887,7 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
auxv[28] = 0;

/* we're now ready to load */
Spawn(exe, fd, sp, e, p, &M->lib);
Spawn(exe, fd, sp, e, p, &M->lib, M->ps.path);
}

int main(int argc, char **argv, char **envp) {
Expand All @@ -900,8 +896,7 @@ int main(int argc, char **argv, char **envp) {
struct ApeLoader *M;
long *sp, *sp2, *auxv;
union ElfEhdrBuf *ebuf;
char *p, *pe, *exe, *prog,
*execfn, *shell, **varpos;
char *p, *pe, *exe, *prog, *execfn, *shell;

/* allocate loader memory in program's arg block */
n = sizeof(struct ApeLoader);
Expand Down Expand Up @@ -965,13 +960,9 @@ int main(int argc, char **argv, char **envp) {

/* getenv("_") is close enough to at_execfn */
execfn = argc > 0 ? argv[0] : 0;
varpos = 0;
for (i = 0; envp[i]; ++i) {
if (envp[i][0] == '_' && envp[i][1] == '=') {
execfn = envp[i] + 2;
} else if (!memcmp(VARNAME, envp[i], VARSIZE)) {
assert(!varpos);
varpos = envp + i;
}
}

Expand All @@ -982,7 +973,7 @@ int main(int argc, char **argv, char **envp) {
/* create new bottom of stack for spawned program
system v abi aligns this on a 16-byte boundary
grows down the alloc by poking the guard pages */
n = (auxv - sp + !varpos + AUXV_WORDS + 1) * sizeof(long);
n = (auxv - sp + AUXV_WORDS + 1) * sizeof(long);
sp2 = (long *)__builtin_alloca(n);
if ((long)sp2 & 15) ++sp2;
for (; n > 0; n -= pagesz) {
Expand All @@ -991,12 +982,6 @@ int main(int argc, char **argv, char **envp) {
memmove(sp2, sp, (auxv - sp) * sizeof(long));
argv = (char **)(sp2 + 1);
envp = (char **)(sp2 + 1 + argc + 1);
if (varpos) {
varpos = (char **)((long *)varpos - sp + sp2);
} else {
varpos = envp + i++;
*(envp + i) = 0;
}
auxv = (long *)(envp + i + 1);
sp = sp2;

Expand Down Expand Up @@ -1061,12 +1046,6 @@ int main(int argc, char **argv, char **envp) {
}
pe = ebuf->buf + rc;

/* inject program executable as first environment variable,
swapping the old first variable for it. */
memmove(M->ps.varname, VARNAME, VARSIZE);
*varpos = *envp;
*envp = M->ps.varname;

/* generate some hard random data */
if ((rc = sys_getentropy(M->rando, sizeof(M->rando))) < 0) {
Pexit(argv[0], rc, "getentropy");
Expand Down
39 changes: 24 additions & 15 deletions libc/calls/getprogramexecutablename.greg.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/assert.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h"
#include "libc/calls/metalfile.internal.h"
Expand Down Expand Up @@ -51,6 +52,12 @@ static inline int IsAlpha(int c) {
}

static inline void InitProgramExecutableNameImpl(void) {
if (__program_executable_name) {
/* already set by the loader */
return;
}
npassert(!issetugid()); // TODO(mrdomino): pathname security
mrdomino marked this conversation as resolved.
Show resolved Hide resolved
// https://github.com/jart/cosmopolitan/issues/991

if (IsWindows()) {
int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16));
Expand All @@ -69,21 +76,20 @@ static inline void InitProgramExecutableNameImpl(void) {
g_prog.u.buf16[2] = '/';
}
tprecode16to8(g_prog.u.buf, sizeof(g_prog.u.buf), g_prog.u.buf16);
return;
goto UseBuf;
}

char c, *q;
if (IsMetal()) {
q = APE_COM_NAME;
goto CopyString;
__program_executable_name = APE_COM_NAME;
return;
}

/* the new-style loader supplies the full program path as the first
environment variable. in the spirit of Postel's Law ("be liberal
in what you accept"), we use __getenv to read it. */
/* the previous loader supplied the full program path as the first
environment variable. */
if ((q = __getenv(__envp, "COSMOPOLITAN_PROGRAM_EXECUTABLE").s)) {
strlcpy(g_prog.u.buf, q, sizeof(g_prog.u.buf));
return;
goto UseBuf;
}

// if argv[0] exists then turn it into an absolute path. we also try
Expand All @@ -107,10 +113,10 @@ static inline void InitProgramExecutableNameImpl(void) {
}
}
*p = 0;
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return;
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) goto UseBuf;
p = WRITE32LE(p, READ32LE(".com"));
*p = 0;
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return;
if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) goto UseBuf;
}

// if getenv("_") exists then use that
Expand All @@ -127,7 +133,7 @@ static inline void InitProgramExecutableNameImpl(void) {
if ((got = sys_readlinkat(AT_FDCWD, "/proc/self/exe", b, n)) > 0 ||
(got = sys_readlinkat(AT_FDCWD, "/proc/curproc/file", b, n)) > 0) {
b[got] = 0;
return;
goto UseBuf;
}
if (IsFreebsd() || IsNetbsd()) {
int cmd[4];
Expand All @@ -140,7 +146,7 @@ static inline void InitProgramExecutableNameImpl(void) {
}
cmd[3] = -1; // current process
if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) {
return;
goto UseBuf;
}
}

Expand All @@ -155,14 +161,17 @@ static inline void InitProgramExecutableNameImpl(void) {
}
}
*p = 0;
return;
goto UseBuf;
}

// if we don't even have that then empty the string
g_prog.u.buf[0] = 0;

UseBuf:
__program_executable_name = g_prog.u.buf;
}

void __InitProgramExecutableName(void) {
static void InitProgramExecutableName(void) {
int e = errno;
InitProgramExecutableNameImpl();
errno = e;
Expand All @@ -172,6 +181,6 @@ void __InitProgramExecutableName(void) {
* Returns absolute path of program.
*/
char *GetProgramExecutableName(void) {
cosmo_once(&g_prog.once, __InitProgramExecutableName);
return g_prog.u.buf;
cosmo_once(&g_prog.once, InitProgramExecutableName);
return __program_executable_name;
}
2 changes: 2 additions & 0 deletions libc/crt/crt.S
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ _start:
// should be set to zero on other platforms
mov x1,x15

// third arg (x2) is the program path passed by ape-m1.c

// switch to c code
bl cosmo
.unreachable
Expand Down
21 changes: 21 additions & 0 deletions libc/nexgen32e/myname2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2023 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/runtime/runtime.h"

char *__program_executable_name;
3 changes: 2 additions & 1 deletion libc/runtime/cosmo2.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ static const char *DecodeMagnum(const char *p, long *r) {
return *r = x, p;
}

wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) {
wontreturn textstartup void cosmo(long *sp, struct Syslib *m1, char *exename) {

// get startup timestamp as early as possible
// its used by --strace and also kprintf() %T
Expand Down Expand Up @@ -108,6 +108,7 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1) {
__envp = envp;
__auxv = auxv;
environ = envp;
__program_executable_name = exename;
program_invocation_name = argv[0];
__oldstack = (intptr_t)sp;

Expand Down
1 change: 1 addition & 0 deletions libc/runtime/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ extern char **__argv;
extern char **__envp;
extern unsigned long *__auxv;
extern intptr_t __oldstack;
extern char *__program_executable_name;
extern uint64_t __nosync;
extern int __strace;
extern int __ftrace;
Expand Down
6 changes: 0 additions & 6 deletions test/libc/calls/getprogramexecutablename_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,3 @@ TEST(GetProgramExecutableName, movedSelf) {
abort();
EXITS(0);
}

void __InitProgramExecutableName(void);

BENCH(GetProgramExecutableName, bench) {
EZBENCH2("Init", donothing, __InitProgramExecutableName());
}