Merge branches *-realpath

Merges branches 'm1-realpath', 'loader-realpath' and
'getprogramexecutablename-realpath' into no-realpath
This commit is contained in:
Jōshin 2023-12-17 15:41:55 +00:00
commit 0734b9c225
No known key found for this signature in database
2 changed files with 66 additions and 82 deletions

View file

@ -545,40 +545,6 @@ __attribute__((__noreturn__)) static void Pexit(int os, const char *c, int rc,
Exit(127, os); 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) { static char AccessCommand(struct PathSearcher *ps, unsigned long pathlen) {
if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0; if (pathlen + 1 + ps->namelen + 1 > sizeof(ps->path)) return 0;
if (pathlen && ps->path[pathlen - 1] != '/') ps->path[pathlen++] = '/'; 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, __attribute__((__noreturn__)) static void Spawn(int os, char *exe, int fd,
char *path, int fd, long *sp, long *sp, unsigned long pagesz,
unsigned long pagesz,
struct ElfEhdr *e, struct ElfEhdr *e,
struct ElfPhdr *p) { struct ElfPhdr *p) {
long rc; long rc;
@ -803,12 +768,12 @@ __attribute__((__noreturn__)) static void Spawn(int os, const char *exe,
Msyscall(dynbase + code, codesize, os); Msyscall(dynbase + code, codesize, os);
/* call program entrypoint */ /* 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, static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
const char *exe, char *path, int fd, long *sp, char *exe, int fd, long *sp, long *auxv,
long *auxv, unsigned long pagesz, int os) { unsigned long pagesz, int os) {
long i, rc; long i, rc;
unsigned size; unsigned size;
struct ElfEhdr *e; struct ElfEhdr *e;
@ -923,7 +888,7 @@ static const char *TryElf(struct ApeLoader *M, union ElfEhdrBuf *ebuf,
} }
/* we're now ready to load */ /* 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) { __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)"); Pexit(os, prog, 0, "not found (maybe chmod +x or ./ needed)");
} else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) { } else if ((fd = Open(exe, O_RDONLY, 0, os)) < 0) {
Pexit(os, exe, fd, "open"); 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) { } else if ((rc = Pread(fd, ebuf->buf, sizeof(ebuf->buf), 0, os)) < 0) {
Pexit(os, exe, rc, "read"); Pexit(os, exe, rc, "read");
} else if ((unsigned long)rc < sizeof(ebuf->ehdr)) { } 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)) { 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));
} }

View file

@ -37,6 +37,7 @@
#define KERN_PROC 14 #define KERN_PROC 14
#define KERN_PROC_PATHNAME_FREEBSD 12 #define KERN_PROC_PATHNAME_FREEBSD 12
#define KERN_PROC_PATHNAME_NETBSD 5 #define KERN_PROC_PATHNAME_NETBSD 5
#define DEV_FD "/dev/fd/"
static struct { static struct {
atomic_uint once; atomic_uint once;
@ -50,15 +51,49 @@ static inline int IsAlpha(int c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); 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) { static inline void InitProgramExecutableNameImpl(void) {
size_t n; size_t n;
ssize_t got; ssize_t got;
char c, *q, *b; char c, *q, *b;
if (__program_executable_name) {
/* already set by the loader */
return;
}
if (IsWindows()) { if (IsWindows()) {
int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16)); int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16));
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
@ -83,6 +118,20 @@ static inline void InitProgramExecutableNameImpl(void) {
return; 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; b = g_prog.u.buf;
n = sizeof(g_prog.u.buf) - 1; n = sizeof(g_prog.u.buf) - 1;
if (IsFreebsd() || IsNetbsd()) { if (IsFreebsd() || IsNetbsd()) {
@ -116,43 +165,15 @@ static inline void InitProgramExecutableNameImpl(void) {
goto UseEmpty; goto UseEmpty;
} }
// if argv[0] exists then turn it into an absolute path. we also try // Try what the loader supplied first. Fall back to argv[0],
// adding a .com suffix since the ape auto-appends it when resolving // then argv[0].com, then $_, then $_.com.
if ((q = __argv[0])) { if (TryPath(__program_executable_name, 0) || TryPath(__argv[0], 1) ||
char *p = g_prog.u.buf; TryPath(__getenv(__envp, "_").s, 1)) {
char *e = p + sizeof(g_prog.u.buf); goto UseBuf;
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;
} }
// give up and just copy argv[0] into it // give up and just copy argv[0] into it
if ((q = __argv[0])) { if ((q = __argv[0])) {
CopyString:
char *p = g_prog.u.buf; char *p = g_prog.u.buf;
char *e = p + sizeof(g_prog.u.buf); char *e = p + sizeof(g_prog.u.buf);
while ((c = *q++)) { while ((c = *q++)) {