From 860bf7ec177db0fc23537425d2b87b2f8c097513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C5=8Dshin?= Date: Fri, 15 Dec 2023 14:24:01 +0000 Subject: [PATCH] Pathname security in GetProgramExecutableName - Tries `KERN_PROC_PATHNAME` first on NetBSD/FreeBSD. - Tries procfs first, but now only on Linux. (You can have /proc on FreeBSD, but then again, /proc could be anything on non-Linux. An option here is to put non-Linux /proc after the issetugid check.) - If `issetugid()` and none of the secure methods have worked, sets the empty string and returns. --- libc/calls/getprogramexecutablename.greg.c | 72 +++++++++++----------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/libc/calls/getprogramexecutablename.greg.c b/libc/calls/getprogramexecutablename.greg.c index 89be34faa..32422e9e8 100644 --- a/libc/calls/getprogramexecutablename.greg.c +++ b/libc/calls/getprogramexecutablename.greg.c @@ -52,13 +52,14 @@ static inline int IsAlpha(int c) { } 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; } - npassert(!issetugid()); // TODO(mrdomino): pathname security - // https://github.com/jart/cosmopolitan/issues/991 - if (IsWindows()) { int n = GetModuleFileName(0, g_prog.u.buf16, ARRAYLEN(g_prog.u.buf16)); for (int i = 0; i < n; ++i) { @@ -78,18 +79,44 @@ static inline void InitProgramExecutableNameImpl(void) { tprecode16to8(g_prog.u.buf, sizeof(g_prog.u.buf), g_prog.u.buf16); goto UseBuf; } - - char c, *q; if (IsMetal()) { __program_executable_name = APE_COM_NAME; return; } + b = g_prog.u.buf; + n = sizeof(g_prog.u.buf) - 1; + if (IsFreebsd() || IsNetbsd()) { + int cmd[4]; + cmd[0] = CTL_KERN; + cmd[1] = KERN_PROC; + if (IsFreebsd()) { + cmd[2] = KERN_PROC_PATHNAME_FREEBSD; + } else { + cmd[2] = KERN_PROC_PATHNAME_NETBSD; + } + cmd[3] = -1; // current process + if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) { + goto UseBuf; + } + } + if (IsLinux()) { + 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; + goto UseBuf; + } + } + + if (issetugid()) { + /* give up prior to using less secure methods */ + goto UseEmpty; + } + /* 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)); - goto UseBuf; + goto CopyString; } // if argv[0] exists then turn it into an absolute path. we also try @@ -120,34 +147,8 @@ static inline void InitProgramExecutableNameImpl(void) { } // if getenv("_") exists then use that - for (char **ep = __envp; (q = *ep); ++ep) { - if (*q++ == '_' && *q++ == '=') { - goto CopyString; - } - } - - // if argv[0] doesn't exist, then fallback to interpreter name - ssize_t got; - char *b = g_prog.u.buf; - size_t n = sizeof(g_prog.u.buf) - 1; - 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; - goto UseBuf; - } - if (IsFreebsd() || IsNetbsd()) { - int cmd[4]; - cmd[0] = CTL_KERN; - cmd[1] = KERN_PROC; - if (IsFreebsd()) { - cmd[2] = KERN_PROC_PATHNAME_FREEBSD; - } else { - cmd[2] = KERN_PROC_PATHNAME_NETBSD; - } - cmd[3] = -1; // current process - if (sys_sysctl(cmd, ARRAYLEN(cmd), b, &n, 0, 0) != -1) { - goto UseBuf; - } + if ((q = __getenv(__envp, "_").s)) { + goto CopyString; } // give up and just copy argv[0] into it @@ -165,6 +166,7 @@ static inline void InitProgramExecutableNameImpl(void) { } // if we don't even have that then empty the string +UseEmpty: g_prog.u.buf[0] = 0; UseBuf: