diff --git a/ape/loader.c b/ape/loader.c index 87369cc42..ece126229 100644 --- a/ape/loader.c +++ b/ape/loader.c @@ -545,40 +545,6 @@ __attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc, Exit(127, os); } -#define PSFD "/proc/self/fd/" - -static int RealPath(int os, int fd, char *path, char **resolved) { - char buf[PATH_MAX]; - int rc; - if (IsLinux()) { - char psfd[sizeof(PSFD) + 19]; - MemMove(psfd, PSFD, sizeof(PSFD) - 1); - Utoa(psfd + sizeof(PSFD) - 1, fd); - rc = SystemCall(-100, (long)psfd, (long)buf, PATH_MAX, 0, 0, 0, - IsAarch64() ? 78 : 267); - if (rc >= 0) { - if (rc == PATH_MAX) { - rc = -36; - } else { - buf[rc] = 0; - } - } - } else if (IsXnu()) { - rc = SystemCall(fd, 50, (long)buf, 0, 0, 0, 0, 92 | 0x2000000); - } else if (IsOpenbsd()) { - rc = SystemCall((long)path, (long)buf, 0, 0, 0, 0, 0, 115); - } else { - *resolved = 0; - return 0; - } - if (rc >= 0) { - MemMove(path, buf, StrLen(buf) + 1); - *resolved = path; - rc = 0; - } - return rc; -} - static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) { if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0; if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; @@ -644,9 +610,8 @@ static char *Commandv(struct PathSearcher *ps, int os, const char *name, } } -__attribute__((__noreturn__)) static void Spawn(int os, const char *exe, - char *path, int fd, long *sp, - unsigned long pagesz, +__attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd, + long *sp, unsigned long pagesz, struct ElfEhdr *e, struct ElfPhdr *p) { long rc; @@ -803,12 +768,12 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe, Msyscall(dynbase + code, codesize, os); /* call program entrypoint */ - Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, path, sp, os); + Launch(IsFreebsd() ? sp : 0, dynbase + e->e_entry, exe, sp, os); } static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf, - const char *exe, char *path, int fd, long *sp, - long *auxv, unsigned long pagesz, int os) { + char *exe, int fd, long *sp, long *auxv, + unsigned long pagesz, int os) { long i, rc; unsigned size; struct ElfEhdr *e; @@ -923,7 +888,7 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf, } /* we're now ready to load */ - Spawn(os, exe, path, fd, sp, pagesz, e, p); + Spawn(os, exe, fd, sp, pagesz, e, p); } __attribute__((__noreturn__)) static void ShowUsage(int os, int fd, int rc) { @@ -1081,8 +1046,6 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)"); } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { Pexit(os, exe, fd, "open"); - } else if ((rc = RealPath(os, fd, exe, &prog)) < 0) { - Pexit(os, exe, rc, "realpath"); } else if ((rc = Pread(fd, ebuf->buf, sizeof(ebuf->buf), 0, os)) < 0) { Pexit(os, exe, rc, "read"); } else if ((unsigned long)rc < sizeof(ebuf->ehdr)) { @@ -1122,9 +1085,9 @@ EXTERN_C __attribute__((__noreturn__)) void ApeLoader(long di, long *sp, } } if (i >= sizeof(ebuf->ehdr)) { - TryElf(M, ebuf, exe, prog, fd, sp, auxv, pagesz, os); + TryElf(M, ebuf, exe, fd, sp, auxv, pagesz, os); } } } - Pexit(os, exe, 0, TryElf(M, ebuf, exe, prog, fd, sp, auxv, pagesz, os)); + Pexit(os, exe, 0, TryElf(M, ebuf, exe, fd, sp, auxv, pagesz, os)); } diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index 05c690b98..c649bb6fd 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -37,6 +37,7 @@ #define KERN_PROC 14 #define KERN_PROC_PATHNAME_FREEBSD 12 #define KERN_PROC_PATHNAME_NETBSD 5 +#define DEV_FD "/dev/fd/" static struct { atomic_uint once; @@ -50,15 +51,49 @@ static inline int IsAlpha(int c) { return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } +// 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) { + if (!q) return 0; + char *p = g_prog.u.buf; + char *e = p + sizeof(g_prog.u.buf); + int c, f_ok; + if ((f_ok = !sys_faccessat(AT_FDCWD, q, F_OK, 0))) { + com = 0; + } else { + if (!com) { + return 0; + } + } + if (*q != '/') { + if (q[0] == '.' && q[1] == '/') { + q += 2; + } + int got = __getcwd(p, e - p - 1 - com * 4); // for / and .com + if (got != -1) { + p += got - 1; + *p++ = '/'; + } + } + while ((c = *q++)) { + if (p + 1 + com * 4 < e) { // for nul and .com + *p++ = c; + } + } + if (f_ok) { + *p = 0; + return 1; + } + p = WRITE32LE(p, READ32LE(".com")); + *p = 0; + return !sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0); +} + static inline void InitProgramExecutableNameImpl(void) { size_t n; ssize_t got; char c, *q, *b; - if (__program_executable_name) { - /* already set by the loader */ - return; - } if (IsWindows()) { int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16)); for (int i = 0; i < n; ++i) { @@ -83,6 +118,20 @@ static inline void InitProgramExecutableNameImpl(void) { return; } + if (issetugid() && __program_executable_name) { + if ((IsNetbsd() || IsOpenbsd() || IsXnu()) /* any others? */ && + !strncmp(DEV_FD, __program_executable_name, sizeof(DEV_FD) - 1) && + isdigit(__program_executable_name[sizeof(DEV_FD)]) && + !strchr(__program_executable_name + sizeof(DEV_FD) + 1, '/')) { + /* loader passed us a secure path */ + return; + } else { + /* we cannot use KERN_PROC_PATHNAME or its ilk in the loader case. they + will report the path of the loader, not the path of the binary. */ + goto UseEmpty; + } + } + b = g_prog.u.buf; n = sizeof(g_prog.u.buf) - 1; if (IsFreebsd() || IsNetbsd()) { @@ -116,43 +165,15 @@ static inline void InitProgramExecutableNameImpl(void) { goto UseEmpty; } - // if argv[0] exists then turn it into an absolute path. we also try - // adding a .com suffix since the ape auto-appends it when resolving - if ((q = __argv[0])) { - char *p = g_prog.u.buf; - char *e = p + sizeof(g_prog.u.buf); - if (*q != '/') { - if (q[0] == '.' && q[1] == '/') { - q += 2; - } - int got = __getcwd(p, e - p - 1 - 4); // for / and .com - if (got != -1) { - p += got - 1; - *p++ = '/'; - } - } - while ((c = *q++)) { - if (p + 1 + 4 < e) { // for nul and .com - *p++ = c; - } - } - *p = 0; - 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)) goto UseBuf; - } - - /* the previous loader supplied the full program path as the first - environment variable. we also try "_". */ - if ((q = __getenv(__envp, "COSMOPOLITAN_PROGRAM_EXECUTABLE").s) || - (q = __getenv(__envp, "_").s)) { - goto CopyString; + // Try what the loader supplied first. Fall back to argv[0], + // then argv[0].com, then $_, then $_.com. + if (TryPath(__program_executable_name, 0) || TryPath(__argv[0], 1) || + TryPath(__getenv(__envp, "_").s, 1)) { + goto UseBuf; } // give up and just copy argv[0] into it if ((q = __argv[0])) { - CopyString: char *p = g_prog.u.buf; char *e = p + sizeof(g_prog.u.buf); while ((c = *q++)) {