From 85086eec1f7df475ca853b26741c389ee31c0691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Sun, 31 Dec 2023 19:11:12 -0500 Subject: [PATCH 1/2] Prepend getcwd to exename early in init --- libc/calls/getprogramexecutablename.greg.c | 52 +++++++++++-------- libc/calls/program_executable_name_init.S | 27 ++++++++++ libc/runtime/cosmo2.c | 3 ++ libc/runtime/internal.h | 1 + .../calls/getprogramexecutablename_test.c | 9 ++++ 5 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 libc/calls/program_executable_name_init.S diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index 1aa6cd2760f..fde2a8dc65d 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -34,6 +34,10 @@ #include "libc/sysv/consts/at.h" #include "libc/sysv/consts/ok.h" +#ifdef __x86_64__ +__static_yoink("_init_program_executable_name"); +#endif + #define CTL_KERN 1 #define KERN_PROC 14 #define KERN_PROC_PATHNAME_FREEBSD 12 @@ -88,31 +92,37 @@ static int OldApeLoader(char *s) { (!strncmp((b = basename(s)), ".ape-", 5) && AllNumDot(b + 5)); } -// if q exists then turn it into an absolute path. we also try adding -// a .com suffix since the ape auto-appends it when resolving -static int TryPath(const char *q, int com) { - char c, *p, *e; - if (!q) return 0; - p = g_prog.u.buf; - e = p + sizeof(g_prog.u.buf); +static char *CopyWithCwd(const char *q, char *p, char *e) { + char c; if (*q != '/') { if (q[0] == '.' && q[1] == '/') { q += 2; } - int got = __getcwd(p, e - p - 1 /* '/' */ - com * 4); + int got = __getcwd(p, e - p - 1 /* '/' */); if (got != -1) { p += got - 1; *p++ = '/'; } } while ((c = *q++)) { - if (p + com * 4 + 1 /* nul */ < e) { + if (p + 1 /* nul */ < e) { *p++ = c; } else { - return 0; + return NULL; } } *p = 0; + return p; +} + +// if q exists then turn it into an absolute path. we also try adding +// a .com suffix since the ape auto-appends it when resolving +static int TryPath(const char *q, int com) { + char *p; + if (!(p = CopyWithCwd(q, g_prog.u.buf, + g_prog.u.buf + sizeof(g_prog.u.buf) - com * 4))) { + return 0; + } if (!sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0)) return 1; if (!com) return 0; p = WRITE32LE(p, READ32LE(".com")); @@ -121,6 +131,16 @@ static int TryPath(const char *q, int com) { return 0; } +// if the loader passed a relative path, prepend cwd to it. +// called early in init. +void __init_program_executable_name(void) { + if (__program_executable_name && + CopyWithCwd(__program_executable_name, g_prog.u.buf, + g_prog.u.buf + sizeof(g_prog.u.buf))) { + __program_executable_name = g_prog.u.buf; + } +} + static inline void InitProgramExecutableNameImpl(void) { size_t n; ssize_t got; @@ -167,18 +187,6 @@ static inline void InitProgramExecutableNameImpl(void) { goto UseEmpty; } } - if (*__program_executable_name == '/') { - return; - } - // loader passed us a relative path; prepend cwd. - if (TryPath(__program_executable_name, 0)) { - goto UseBuf; - } - /* if TryPath fails, it probably failed because getcwd() was too long. - we are out of options now; KERN_PROC_PATHNAME et al will return the - name of the loader not the binary, and argv et al will at best have - the same problem. just use the relative path we got from the loader - as-is, and accept that if we chdir then things will break. */ return; } diff --git a/libc/calls/program_executable_name_init.S b/libc/calls/program_executable_name_init.S new file mode 100644 index 00000000000..99ed4db3ed3 --- /dev/null +++ b/libc/calls/program_executable_name_init.S @@ -0,0 +1,27 @@ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│ vi: set noet ft=asm ts=8 sw=8 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/macros.internal.h" + + .init.start 305,_init_program_executable_name + push %rdi + push %rsi + call __init_program_executable_name + pop %rsi + pop %rdi + .init.end 305,_init_program_executable_name diff --git a/libc/runtime/cosmo2.c b/libc/runtime/cosmo2.c index c1f5b9b34e6..841c37d2381 100644 --- a/libc/runtime/cosmo2.c +++ b/libc/runtime/cosmo2.c @@ -172,6 +172,9 @@ wontreturn textstartup void cosmo(long *sp, struct Syslib *m1, char *exename, // initialize file system __init_fds(argc, argv, envp); + // prepend cwd to executable path + __init_program_executable_name(); + __enable_tls(); #if 0 diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index a77d85ff0e0..6484d5cdd56 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -47,6 +47,7 @@ void *__mmap_unlocked(void *, size_t, int, int, int, int64_t); int __munmap_unlocked(char *, size_t); void __on_arithmetic_overflow(void); void __init_fds(int, char **, char **); +void __init_program_executable_name(void); COSMOPOLITAN_C_END_ #endif /* ANSI */ diff --git a/test/libc/calls/getprogramexecutablename_test.c b/test/libc/calls/getprogramexecutablename_test.c index 99294ce95e0..73bc8d525bb 100644 --- a/test/libc/calls/getprogramexecutablename_test.c +++ b/test/libc/calls/getprogramexecutablename_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/metalfile.internal.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/limits.h" #include "libc/runtime/runtime.h" @@ -57,6 +58,9 @@ void SetUpOnce(void) { __attribute__((__constructor__)) static void Child(int argc, char *argv[]) { if (argc >= 2 && !strcmp(argv[1], "Child")) { + if (sys_chdir("/")) { + exit(122); + } if (strcmp(argv[2], GetProgramExecutableName())) { exit(123); } @@ -138,4 +142,9 @@ TEST(GetProgramExecutableName, movedSelf) { execve(buf, (char *[]){"hello", "Child", buf, "hello", 0}, (char *[]){0}); abort(); EXITS(0); + SPAWN(fork); + execve("./test", (char *[]){"hello", "Child", buf, "hello", 0}, + (char *[]){0}); + abort(); + EXITS(0); } From 9de9dcf3c1255e10566f249c820e33f59458036b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Sun, 31 Dec 2023 19:20:18 -0500 Subject: [PATCH 2/2] Save a copy if loader exename is absolute --- libc/calls/getprogramexecutablename.greg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index fde2a8dc65d..ad85890c9b6 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -134,7 +134,7 @@ static int TryPath(const char *q, int com) { // if the loader passed a relative path, prepend cwd to it. // called early in init. void __init_program_executable_name(void) { - if (__program_executable_name && + if (__program_executable_name && *__program_executable_name != '/' && CopyWithCwd(__program_executable_name, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf))) { __program_executable_name = g_prog.u.buf;