diff --git a/lib/libc/csu/ignore_init.c b/lib/libc/csu/ignore_init.c index 60c45d7e735f..346409e3f370 100644 --- a/lib/libc/csu/ignore_init.c +++ b/lib/libc/csu/ignore_init.c @@ -1,184 +1,182 @@ /*- * SPDX-License-Identifier: BSD-1-Clause * * Copyright 2012 Konstantin Belousov * Copyright (c) 2018, 2023 The FreeBSD Foundation * * Parts of this software was developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include "libc_private.h" extern void (*__preinit_array_start[])(int, char **, char **) __hidden; extern void (*__preinit_array_end[])(int, char **, char **) __hidden; extern void (*__init_array_start[])(int, char **, char **) __hidden; extern void (*__init_array_end[])(int, char **, char **) __hidden; extern void (*__fini_array_start[])(void) __hidden; extern void (*__fini_array_end[])(void) __hidden; extern void _fini(void) __hidden; extern void _init(void) __hidden; extern int _DYNAMIC; #pragma weak _DYNAMIC #if defined(CRT_IRELOC_RELA) extern const Elf_Rela __rela_iplt_start[] __weak_symbol __hidden; extern const Elf_Rela __rela_iplt_end[] __weak_symbol __hidden; #include "reloc.c" static void process_irelocs(void) { const Elf_Rela *r; for (r = &__rela_iplt_start[0]; r < &__rela_iplt_end[0]; r++) crt1_handle_rela(r); } #elif defined(CRT_IRELOC_REL) extern const Elf_Rel __rel_iplt_start[] __weak_symbol __hidden; extern const Elf_Rel __rel_iplt_end[] __weak_symbol __hidden; #include "reloc.c" static void process_irelocs(void) { const Elf_Rel *r; for (r = &__rel_iplt_start[0]; r < &__rel_iplt_end[0]; r++) crt1_handle_rel(r); } #elif defined(CRT_IRELOC_SUPPRESS) #else #error "Define platform reloc type" #endif static void finalizer(void) { void (*fn)(void); size_t array_size, n; array_size = __fini_array_end - __fini_array_start; for (n = array_size; n > 0; n--) { fn = __fini_array_start[n - 1]; if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) (fn)(); } _fini(); } static void handle_static_init(int argc, char **argv, char **env) { void (*fn)(int, char **, char **); size_t array_size, n; if (&_DYNAMIC != NULL) return; atexit(finalizer); array_size = __preinit_array_end - __preinit_array_start; for (n = 0; n < array_size; n++) { fn = __preinit_array_start[n]; if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) fn(argc, argv, env); } _init(); array_size = __init_array_end - __init_array_start; for (n = 0; n < array_size; n++) { fn = __init_array_start[n]; if ((uintptr_t)fn != 0 && (uintptr_t)fn != 1) fn(argc, argv, env); } } -extern char **environ; - static void handle_argv(int argc, char *argv[], char **env) { const char *s; if (environ == NULL) environ = env; if (argc > 0 && argv[0] != NULL) { __progname = argv[0]; for (s = __progname; *s != '\0'; s++) { if (*s == '/') __progname = s + 1; } } } void __libc_start1(int argc, char *argv[], char *env[], void (*cleanup)(void), int (*mainX)(int, char *[], char *[])) { handle_argv(argc, argv, env); if (&_DYNAMIC != NULL) { atexit(cleanup); } else { #ifndef CRT_IRELOC_SUPPRESS INIT_IRELOCS; process_irelocs(); #endif _init_tls(); } handle_static_init(argc, argv, env); exit(mainX(argc, argv, env)); } /* XXXKIB _mcleanup and monstartup defs */ extern void _mcleanup(void); extern void monstartup(void *, void *); void __libc_start1_gcrt(int argc, char *argv[], char *env[], void (*cleanup)(void), int (*mainX)(int, char *[], char *[]), int *eprolp, int *etextp) { handle_argv(argc, argv, env); if (&_DYNAMIC != NULL) { atexit(cleanup); } else { #ifndef CRT_IRELOC_SUPPRESS INIT_IRELOCS; process_irelocs(); #endif _init_tls(); } atexit(_mcleanup); monstartup(eprolp, etextp); handle_static_init(argc, argv, env); exit(mainX(argc, argv, env)); } diff --git a/lib/libc/gen/auxv.c b/lib/libc/gen/auxv.c index 2f043f8814cf..29b3d6929144 100644 --- a/lib/libc/gen/auxv.c +++ b/lib/libc/gen/auxv.c @@ -1,407 +1,406 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2010, 2012 Konstantin Belousov . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" -extern char **environ; extern int _DYNAMIC; #pragma weak _DYNAMIC void *__elf_aux_vector; static pthread_once_t aux_vector_once = PTHREAD_ONCE_INIT; static void init_aux_vector_once(void) { Elf_Addr *sp; sp = (Elf_Addr *)environ; while (*sp++ != 0) ; __elf_aux_vector = (Elf_Auxinfo *)sp; } void __init_elf_aux_vector(void) { if (&_DYNAMIC != NULL) return; _once(&aux_vector_once, init_aux_vector_once); } static pthread_once_t aux_once = PTHREAD_ONCE_INIT; static int pagesize, osreldate, canary_len, ncpus, pagesizes_len, bsdflags; static int hwcap_present, hwcap2_present; static char *canary, *pagesizes, *execpath; static void *ps_strings, *timekeep; static u_long hwcap, hwcap2; static void *fxrng_seed_version; static u_long usrstackbase, usrstacklim; #ifdef __powerpc__ static int powerpc_new_auxv_format = 0; static void _init_aux_powerpc_fixup(void); int _powerpc_elf_aux_info(int, void *, int); #endif static void init_aux(void) { Elf_Auxinfo *aux; for (aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) { switch (aux->a_type) { case AT_BSDFLAGS: bsdflags = aux->a_un.a_val; break; case AT_CANARY: canary = (char *)(aux->a_un.a_ptr); break; case AT_CANARYLEN: canary_len = aux->a_un.a_val; break; case AT_EXECPATH: execpath = (char *)(aux->a_un.a_ptr); break; case AT_HWCAP: hwcap_present = 1; hwcap = (u_long)(aux->a_un.a_val); break; case AT_HWCAP2: hwcap2_present = 1; hwcap2 = (u_long)(aux->a_un.a_val); break; case AT_PAGESIZES: pagesizes = (char *)(aux->a_un.a_ptr); break; case AT_PAGESIZESLEN: pagesizes_len = aux->a_un.a_val; break; case AT_PAGESZ: pagesize = aux->a_un.a_val; break; case AT_OSRELDATE: osreldate = aux->a_un.a_val; break; case AT_NCPUS: ncpus = aux->a_un.a_val; break; case AT_TIMEKEEP: timekeep = aux->a_un.a_ptr; break; case AT_PS_STRINGS: ps_strings = aux->a_un.a_ptr; break; case AT_FXRNG: fxrng_seed_version = aux->a_un.a_ptr; break; case AT_USRSTACKBASE: usrstackbase = aux->a_un.a_val; break; case AT_USRSTACKLIM: usrstacklim = aux->a_un.a_val; break; #ifdef __powerpc__ /* * Since AT_STACKPROT is always set, and the common * value 23 is mutually exclusive with the legacy powerpc * value 21, the existence of AT_STACKPROT proves we are * on the common format. */ case AT_STACKPROT: /* 23 */ powerpc_new_auxv_format = 1; break; #endif } } #ifdef __powerpc__ if (!powerpc_new_auxv_format) _init_aux_powerpc_fixup(); #endif } #ifdef __powerpc__ static void _init_aux_powerpc_fixup(void) { Elf_Auxinfo *aux; /* * Before 1300070, PowerPC platforms had nonstandard numbering for * the aux vector. When running old binaries, the kernel will pass * the vector using the old numbering. Reload affected variables. */ for (aux = __elf_aux_vector; aux->a_type != AT_NULL; aux++) { switch (aux->a_type) { case AT_OLD_CANARY: canary = (char *)(aux->a_un.a_ptr); break; case AT_OLD_CANARYLEN: canary_len = aux->a_un.a_val; break; case AT_OLD_EXECPATH: execpath = (char *)(aux->a_un.a_ptr); break; case AT_OLD_PAGESIZES: pagesizes = (char *)(aux->a_un.a_ptr); break; case AT_OLD_PAGESIZESLEN: pagesizes_len = aux->a_un.a_val; break; case AT_OLD_OSRELDATE: osreldate = aux->a_un.a_val; break; case AT_OLD_NCPUS: ncpus = aux->a_un.a_val; break; } } } int _powerpc_elf_aux_info(int aux, void *buf, int buflen) { /* * If we are in the old auxv format, we need to translate the aux * parameter of elf_aux_info() calls into the common auxv format. * Internal libc calls always use the common format, and they * directly call _elf_aux_info instead of using the weak symbol. */ if (!powerpc_new_auxv_format) { switch (aux) { case AT_OLD_EXECPATH: aux = AT_EXECPATH; break; case AT_OLD_CANARY: aux = AT_CANARY; break; case AT_OLD_CANARYLEN: aux = AT_CANARYLEN; break; case AT_OLD_OSRELDATE: aux = AT_OSRELDATE; break; case AT_OLD_NCPUS: aux = AT_NCPUS; break; case AT_OLD_PAGESIZES: aux = AT_PAGESIZES; break; case AT_OLD_PAGESIZESLEN: aux = AT_PAGESIZESLEN; break; case AT_OLD_STACKPROT: aux = AT_STACKPROT; break; } } return _elf_aux_info(aux, buf, buflen); } __weak_reference(_powerpc_elf_aux_info, elf_aux_info); #else __weak_reference(_elf_aux_info, elf_aux_info); #endif int _elf_aux_info(int aux, void *buf, int buflen) { int res; __init_elf_aux_vector(); if (__elf_aux_vector == NULL) return (ENOSYS); _once(&aux_once, init_aux); if (buflen < 0) return (EINVAL); switch (aux) { case AT_CANARY: if (canary != NULL && canary_len >= buflen) { memcpy(buf, canary, buflen); memset(canary, 0, canary_len); canary = NULL; res = 0; } else res = ENOENT; break; case AT_EXECPATH: if (execpath == NULL) res = ENOENT; else if (buf == NULL) res = EINVAL; else { if (strlcpy(buf, execpath, buflen) >= (unsigned int)buflen) res = EINVAL; else res = 0; } break; case AT_HWCAP: if (hwcap_present && buflen == sizeof(u_long)) { *(u_long *)buf = hwcap; res = 0; } else res = ENOENT; break; case AT_HWCAP2: if (hwcap2_present && buflen == sizeof(u_long)) { *(u_long *)buf = hwcap2; res = 0; } else res = ENOENT; break; case AT_PAGESIZES: if (pagesizes != NULL && pagesizes_len >= buflen) { memcpy(buf, pagesizes, buflen); res = 0; } else res = ENOENT; break; case AT_PAGESZ: if (buflen == sizeof(int)) { if (pagesize != 0) { *(int *)buf = pagesize; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_OSRELDATE: if (buflen == sizeof(int)) { if (osreldate != 0) { *(int *)buf = osreldate; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_NCPUS: if (buflen == sizeof(int)) { if (ncpus != 0) { *(int *)buf = ncpus; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_TIMEKEEP: if (buflen == sizeof(void *)) { if (timekeep != NULL) { *(void **)buf = timekeep; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_BSDFLAGS: if (buflen == sizeof(int)) { *(int *)buf = bsdflags; res = 0; } else res = EINVAL; break; case AT_PS_STRINGS: if (buflen == sizeof(void *)) { if (ps_strings != NULL) { *(void **)buf = ps_strings; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_FXRNG: if (buflen == sizeof(void *)) { if (fxrng_seed_version != NULL) { *(void **)buf = fxrng_seed_version; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_USRSTACKBASE: if (buflen == sizeof(u_long)) { if (usrstackbase != 0) { *(u_long *)buf = usrstackbase; res = 0; } else res = ENOENT; } else res = EINVAL; break; case AT_USRSTACKLIM: if (buflen == sizeof(u_long)) { if (usrstacklim != 0) { *(u_long *)buf = usrstacklim; res = 0; } else res = ENOENT; } else res = EINVAL; break; default: res = ENOENT; break; } return (res); } diff --git a/lib/libc/gen/exec.c b/lib/libc/gen/exec.c index 2c703bb4e73e..f1d69e147719 100644 --- a/lib/libc/gen/exec.c +++ b/lib/libc/gen/exec.c @@ -1,306 +1,304 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __SCCSID("@(#)exec.c 8.1 (Berkeley) 6/4/93"); __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" -extern char **environ; - static const char execvPe_err_preamble[] = "execvP: "; static const char execvPe_err_trailer[] = ": path too long\n"; int execl(const char *name, const char *arg, ...) { va_list ap; const char **argv; int n; va_start(ap, arg); n = 1; while (va_arg(ap, char *) != NULL) n++; va_end(ap); argv = alloca((n + 1) * sizeof(*argv)); if (argv == NULL) { errno = ENOMEM; return (-1); } va_start(ap, arg); n = 1; argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; va_end(ap); return (_execve(name, __DECONST(char **, argv), environ)); } int execle(const char *name, const char *arg, ...) { va_list ap; const char **argv; char **envp; int n; va_start(ap, arg); n = 1; while (va_arg(ap, char *) != NULL) n++; va_end(ap); argv = alloca((n + 1) * sizeof(*argv)); if (argv == NULL) { errno = ENOMEM; return (-1); } va_start(ap, arg); n = 1; argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; envp = va_arg(ap, char **); va_end(ap); return (_execve(name, __DECONST(char **, argv), envp)); } int execlp(const char *name, const char *arg, ...) { va_list ap; const char **argv; int n; va_start(ap, arg); n = 1; while (va_arg(ap, char *) != NULL) n++; va_end(ap); argv = alloca((n + 1) * sizeof(*argv)); if (argv == NULL) { errno = ENOMEM; return (-1); } va_start(ap, arg); n = 1; argv[0] = arg; while ((argv[n] = va_arg(ap, char *)) != NULL) n++; va_end(ap); return (execvp(name, __DECONST(char **, argv))); } int execv(const char *name, char * const *argv) { (void)_execve(name, argv, environ); return (-1); } int execvp(const char *name, char * const *argv) { return (_execvpe(name, argv, environ)); } static int execvPe(const char *name, const char *path, char * const *argv, char * const *envp) { const char **memp; size_t cnt, lp, ln; int eacces, save_errno; char buf[MAXPATHLEN]; const char *bp, *np, *op, *p; struct stat sb; eacces = 0; /* If it's an absolute or relative path name, it's easy. */ if (strchr(name, '/')) { bp = name; op = NULL; goto retry; } bp = buf; /* If it's an empty path name, fail in the usual POSIX way. */ if (*name == '\0') { errno = ENOENT; return (-1); } op = path; ln = strlen(name); while (op != NULL) { np = strchrnul(op, ':'); /* * It's a SHELL path -- double, leading and trailing colons * mean the current directory. */ if (np == op) { /* Empty component. */ p = "."; lp = 1; } else { /* Non-empty component. */ p = op; lp = np - op; } /* Advance to the next component or terminate after this. */ if (*np == '\0') op = NULL; else op = np + 1; /* * If the path is too long complain. This is a possible * security issue; given a way to make the path too long * the user may execute the wrong program. */ if (lp + ln + 2 > sizeof(buf)) { (void)_write(STDERR_FILENO, execvPe_err_preamble, sizeof(execvPe_err_preamble) - 1); (void)_write(STDERR_FILENO, p, lp); (void)_write(STDERR_FILENO, execvPe_err_trailer, sizeof(execvPe_err_trailer) - 1); continue; } bcopy(p, buf, lp); buf[lp] = '/'; bcopy(name, buf + lp + 1, ln); buf[lp + ln + 1] = '\0'; retry: (void)_execve(bp, argv, envp); switch (errno) { case E2BIG: goto done; case ELOOP: case ENAMETOOLONG: case ENOENT: break; case ENOEXEC: for (cnt = 0; argv[cnt]; ++cnt) ; /* * cnt may be 0 above; always allocate at least * 3 entries so that we can at least fit "sh", bp, and * the NULL terminator. We can rely on cnt to take into * account the NULL terminator in all other scenarios, * as we drop argv[0]. */ memp = alloca(MAX(3, cnt + 2) * sizeof(char *)); if (memp == NULL) { /* errno = ENOMEM; XXX override ENOEXEC? */ goto done; } if (cnt > 0) { memp[0] = argv[0]; memp[1] = bp; bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); } else { memp[0] = "sh"; memp[1] = bp; memp[2] = NULL; } (void)_execve(_PATH_BSHELL, __DECONST(char **, memp), envp); goto done; case ENOMEM: goto done; case ENOTDIR: break; case ETXTBSY: /* * We used to retry here, but sh(1) doesn't. */ goto done; default: /* * EACCES may be for an inaccessible directory or * a non-executable file. Call stat() to decide * which. This also handles ambiguities for EFAULT * and EIO, and undocumented errors like ESTALE. * We hope that the race for a stat() is unimportant. */ save_errno = errno; if (stat(bp, &sb) != 0) break; if (save_errno == EACCES) { eacces = 1; continue; } errno = save_errno; goto done; } } if (eacces) errno = EACCES; else errno = ENOENT; done: return (-1); } int execvP(const char *name, const char *path, char * const argv[]) { return execvPe(name, path, argv, environ); } int _execvpe(const char *name, char * const argv[], char * const envp[]) { const char *path; /* Get the path we're searching. */ if ((path = getenv("PATH")) == NULL) path = _PATH_DEFPATH; return (execvPe(name, path, argv, envp)); } diff --git a/lib/libc/gen/popen.c b/lib/libc/gen/popen.c index c5373077f3ae..f0b1a61c2803 100644 --- a/lib/libc/gen/popen.c +++ b/lib/libc/gen/popen.c @@ -1,229 +1,227 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __SCCSID("@(#)popen.c 8.3 (Berkeley) 5/3/95"); __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" -extern char **environ; - struct pid { SLIST_ENTRY(pid) next; FILE *fp; pid_t pid; }; static SLIST_HEAD(, pid) pidlist = SLIST_HEAD_INITIALIZER(pidlist); static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER; #define THREAD_LOCK() if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex) #define THREAD_UNLOCK() if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex) FILE * popen(const char *command, const char *type) { struct pid *cur; FILE *iop; int pdes[2], pid, twoway, cloexec; int pdes_unused_in_parent; char *argv[4]; struct pid *p; cloexec = strchr(type, 'e') != NULL; /* * Lite2 introduced two-way popen() pipes using _socketpair(). * FreeBSD's pipe() is bidirectional, so we use that. */ if (strchr(type, '+')) { twoway = 1; type = "r+"; } else { twoway = 0; if ((*type != 'r' && *type != 'w') || (type[1] && (type[1] != 'e' || type[2]))) return (NULL); } if (pipe2(pdes, O_CLOEXEC) < 0) return (NULL); if ((cur = malloc(sizeof(struct pid))) == NULL) { (void)_close(pdes[0]); (void)_close(pdes[1]); return (NULL); } if (*type == 'r') { iop = fdopen(pdes[0], type); pdes_unused_in_parent = pdes[1]; } else { iop = fdopen(pdes[1], type); pdes_unused_in_parent = pdes[0]; } if (iop == NULL) { (void)_close(pdes[0]); (void)_close(pdes[1]); free(cur); return (NULL); } argv[0] = "sh"; argv[1] = "-c"; argv[2] = (char *)command; argv[3] = NULL; THREAD_LOCK(); switch (pid = vfork()) { case -1: /* Error. */ THREAD_UNLOCK(); /* * The _close() closes the unused end of pdes[], while * the fclose() closes the used end of pdes[], *and* cleans * up iop. */ (void)_close(pdes_unused_in_parent); free(cur); (void)fclose(iop); return (NULL); /* NOTREACHED */ case 0: /* Child. */ if (*type == 'r') { /* * The _dup2() to STDIN_FILENO is repeated to avoid * writing to pdes[1], which might corrupt the * parent's copy. This isn't good enough in * general, since the _exit() is no return, so * the compiler is free to corrupt all the local * variables. */ if (pdes[1] != STDOUT_FILENO) { (void)_dup2(pdes[1], STDOUT_FILENO); if (twoway) (void)_dup2(STDOUT_FILENO, STDIN_FILENO); } else if (twoway && (pdes[1] != STDIN_FILENO)) { (void)_dup2(pdes[1], STDIN_FILENO); (void)_fcntl(pdes[1], F_SETFD, 0); } else (void)_fcntl(pdes[1], F_SETFD, 0); } else { if (pdes[0] != STDIN_FILENO) { (void)_dup2(pdes[0], STDIN_FILENO); } else (void)_fcntl(pdes[0], F_SETFD, 0); } SLIST_FOREACH(p, &pidlist, next) (void)_close(fileno(p->fp)); _execve(_PATH_BSHELL, argv, environ); _exit(127); /* NOTREACHED */ } THREAD_UNLOCK(); /* Parent. */ (void)_close(pdes_unused_in_parent); /* Link into list of file descriptors. */ cur->fp = iop; cur->pid = pid; THREAD_LOCK(); SLIST_INSERT_HEAD(&pidlist, cur, next); THREAD_UNLOCK(); /* * To guard against undesired fd passing with concurrent calls, * only clear the close-on-exec flag after linking the file into * the list which will cause an explicit close. */ if (!cloexec) (void)_fcntl(fileno(iop), F_SETFD, 0); return (iop); } /* * pclose -- * Pclose returns -1 if stream is not associated with a `popened' command, * if already `pclosed', or waitpid returns an error. */ int pclose(FILE *iop) { struct pid *cur, *last = NULL; int pstat; pid_t pid; /* * Find the appropriate file pointer and remove it from the list. */ THREAD_LOCK(); SLIST_FOREACH(cur, &pidlist, next) { if (cur->fp == iop) break; last = cur; } if (cur == NULL) { THREAD_UNLOCK(); return (-1); } if (last == NULL) SLIST_REMOVE_HEAD(&pidlist, next); else SLIST_REMOVE_AFTER(last, next); THREAD_UNLOCK(); (void)fclose(iop); do { pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0); } while (pid == -1 && errno == EINTR); free(cur); return (pid == -1 ? -1 : pstat); } diff --git a/lib/libc/gen/posix_spawn.c b/lib/libc/gen/posix_spawn.c index 909db9a60a02..7dc0ade5b309 100644 --- a/lib/libc/gen/posix_spawn.c +++ b/lib/libc/gen/posix_spawn.c @@ -1,677 +1,675 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Ed Schouten * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" -extern char **environ; - struct __posix_spawnattr { short sa_flags; pid_t sa_pgroup; struct sched_param sa_schedparam; int sa_schedpolicy; sigset_t sa_sigdefault; sigset_t sa_sigmask; }; struct __posix_spawn_file_actions { STAILQ_HEAD(, __posix_spawn_file_actions_entry) fa_list; }; typedef struct __posix_spawn_file_actions_entry { STAILQ_ENTRY(__posix_spawn_file_actions_entry) fae_list; enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE, FAE_CHDIR, FAE_FCHDIR, FAE_CLOSEFROM, } fae_action; int fae_fildes; union { struct { char *path; #define fae_path fae_data.open.path int oflag; #define fae_oflag fae_data.open.oflag mode_t mode; #define fae_mode fae_data.open.mode } open; struct { int newfildes; #define fae_newfildes fae_data.dup2.newfildes } dup2; } fae_data; } posix_spawn_file_actions_entry_t; /* * Spawn routines */ static int process_spawnattr(const posix_spawnattr_t sa) { struct sigaction sigact = { .sa_flags = 0, .sa_handler = SIG_DFL }; int i; /* * POSIX doesn't really describe in which order everything * should be set. We'll just set them in the order in which they * are mentioned. */ /* Set process group */ if (sa->sa_flags & POSIX_SPAWN_SETPGROUP) { if (setpgid(0, sa->sa_pgroup) != 0) return (errno); } /* Set scheduler policy */ if (sa->sa_flags & POSIX_SPAWN_SETSCHEDULER) { if (sched_setscheduler(0, sa->sa_schedpolicy, &sa->sa_schedparam) != 0) return (errno); } else if (sa->sa_flags & POSIX_SPAWN_SETSCHEDPARAM) { if (sched_setparam(0, &sa->sa_schedparam) != 0) return (errno); } /* Reset user ID's */ if (sa->sa_flags & POSIX_SPAWN_RESETIDS) { if (setegid(getgid()) != 0) return (errno); if (seteuid(getuid()) != 0) return (errno); } /* * Set signal masks/defaults. * Use unwrapped syscall, libthr is in undefined state after vfork(). */ if (sa->sa_flags & POSIX_SPAWN_SETSIGMASK) { __sys_sigprocmask(SIG_SETMASK, &sa->sa_sigmask, NULL); } if (sa->sa_flags & POSIX_SPAWN_SETSIGDEF) { for (i = 1; i <= _SIG_MAXSIG; i++) { if (sigismember(&sa->sa_sigdefault, i)) if (__sys_sigaction(i, &sigact, NULL) != 0) return (errno); } } return (0); } static int process_file_actions_entry(posix_spawn_file_actions_entry_t *fae) { int fd, saved_errno; switch (fae->fae_action) { case FAE_OPEN: /* Perform an open(), make it use the right fd */ fd = _open(fae->fae_path, fae->fae_oflag, fae->fae_mode); if (fd < 0) return (errno); if (fd != fae->fae_fildes) { if (_dup2(fd, fae->fae_fildes) == -1) { saved_errno = errno; (void)_close(fd); return (saved_errno); } if (_close(fd) != 0) { if (errno == EBADF) return (EBADF); } } if (_fcntl(fae->fae_fildes, F_SETFD, 0) == -1) return (errno); break; case FAE_DUP2: /* Perform a dup2() */ if (_dup2(fae->fae_fildes, fae->fae_newfildes) == -1) return (errno); if (_fcntl(fae->fae_newfildes, F_SETFD, 0) == -1) return (errno); break; case FAE_CLOSE: /* Perform a close(), do not fail if already closed */ (void)_close(fae->fae_fildes); break; case FAE_CHDIR: if (chdir(fae->fae_path) != 0) return (errno); break; case FAE_FCHDIR: if (fchdir(fae->fae_fildes) != 0) return (errno); break; case FAE_CLOSEFROM: closefrom(fae->fae_fildes); break; } return (0); } static int process_file_actions(const posix_spawn_file_actions_t fa) { posix_spawn_file_actions_entry_t *fae; int error; /* Replay all file descriptor modifications */ STAILQ_FOREACH(fae, &fa->fa_list, fae_list) { error = process_file_actions_entry(fae); if (error) return (error); } return (0); } struct posix_spawn_args { const char *path; const posix_spawn_file_actions_t *fa; const posix_spawnattr_t *sa; char * const * argv; char * const * envp; int use_env_path; volatile int error; }; #define PSPAWN_STACK_ALIGNMENT 16 #define PSPAWN_STACK_ALIGNBYTES (PSPAWN_STACK_ALIGNMENT - 1) #define PSPAWN_STACK_ALIGN(sz) \ (((sz) + PSPAWN_STACK_ALIGNBYTES) & ~PSPAWN_STACK_ALIGNBYTES) #if defined(__i386__) || defined(__amd64__) /* * Below we'll assume that _RFORK_THREAD_STACK_SIZE is appropriately aligned for * the posix_spawn() case where we do not end up calling _execvpe and won't ever * try to allocate space on the stack for argv[]. */ #define _RFORK_THREAD_STACK_SIZE 4096 _Static_assert((_RFORK_THREAD_STACK_SIZE % PSPAWN_STACK_ALIGNMENT) == 0, "Inappropriate stack size alignment"); #endif static int _posix_spawn_thr(void *data) { struct posix_spawn_args *psa; char * const *envp; psa = data; if (psa->sa != NULL) { psa->error = process_spawnattr(*psa->sa); if (psa->error) _exit(127); } if (psa->fa != NULL) { psa->error = process_file_actions(*psa->fa); if (psa->error) _exit(127); } envp = psa->envp != NULL ? psa->envp : environ; if (psa->use_env_path) _execvpe(psa->path, psa->argv, envp); else _execve(psa->path, psa->argv, envp); psa->error = errno; /* This is called in such a way that it must not exit. */ _exit(127); } static int do_posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *fa, const posix_spawnattr_t *sa, char * const argv[], char * const envp[], int use_env_path) { struct posix_spawn_args psa; pid_t p; #ifdef _RFORK_THREAD_STACK_SIZE char *stack; size_t cnt, stacksz; stacksz = _RFORK_THREAD_STACK_SIZE; if (use_env_path) { /* * We need to make sure we have enough room on the stack for the * potential alloca() in execvPe if it gets kicked back an * ENOEXEC from execve(2), plus the original buffer we gave * ourselves; this protects us in the event that the caller * intentionally or inadvertently supplies enough arguments to * make us blow past the stack we've allocated from it. */ for (cnt = 0; argv[cnt] != NULL; ++cnt) ; stacksz += MAX(3, cnt + 2) * sizeof(char *); stacksz = PSPAWN_STACK_ALIGN(stacksz); } /* * aligned_alloc is not safe to use here, because we can't guarantee * that aligned_alloc and free will be provided by the same * implementation. We've actively hit at least one application that * will provide its own malloc/free but not aligned_alloc leading to * a free by the wrong allocator. */ stack = malloc(stacksz); if (stack == NULL) return (ENOMEM); stacksz = (((uintptr_t)stack + stacksz) & ~PSPAWN_STACK_ALIGNBYTES) - (uintptr_t)stack; #endif psa.path = path; psa.fa = fa; psa.sa = sa; psa.argv = argv; psa.envp = envp; psa.use_env_path = use_env_path; psa.error = 0; /* * Passing RFSPAWN to rfork(2) gives us effectively a vfork that drops * non-ignored signal handlers. We'll fall back to the slightly less * ideal vfork(2) if we get an EINVAL from rfork -- this should only * happen with newer libc on older kernel that doesn't accept * RFSPAWN. */ #ifdef _RFORK_THREAD_STACK_SIZE /* * x86 stores the return address on the stack, so rfork(2) cannot work * as-is because the child would clobber the return address om the * parent. Because of this, we must use rfork_thread instead while * almost every other arch stores the return address in a register. */ p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); free(stack); #else p = rfork(RFSPAWN); if (p == 0) /* _posix_spawn_thr does not return */ _posix_spawn_thr(&psa); #endif /* * The above block should leave us in a state where we've either * succeeded and we're ready to process the results, or we need to * fallback to vfork() if the kernel didn't like RFSPAWN. */ if (p == -1 && errno == EINVAL) { p = vfork(); if (p == 0) /* _posix_spawn_thr does not return */ _posix_spawn_thr(&psa); } if (p == -1) return (errno); if (psa.error != 0) /* Failed; ready to reap */ _waitpid(p, NULL, WNOHANG); else if (pid != NULL) /* exec succeeded */ *pid = p; return (psa.error); } int posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *fa, const posix_spawnattr_t *sa, char * const argv[], char * const envp[]) { return (do_posix_spawn(pid, path, fa, sa, argv, envp, 0)); } int posix_spawnp(pid_t *pid, const char *path, const posix_spawn_file_actions_t *fa, const posix_spawnattr_t *sa, char * const argv[], char * const envp[]) { return (do_posix_spawn(pid, path, fa, sa, argv, envp, 1)); } /* * File descriptor actions */ int posix_spawn_file_actions_init(posix_spawn_file_actions_t *ret) { posix_spawn_file_actions_t fa; fa = malloc(sizeof(struct __posix_spawn_file_actions)); if (fa == NULL) return (-1); STAILQ_INIT(&fa->fa_list); *ret = fa; return (0); } int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *fa) { posix_spawn_file_actions_entry_t *fae; while ((fae = STAILQ_FIRST(&(*fa)->fa_list)) != NULL) { /* Remove file action entry from the queue */ STAILQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list); /* Deallocate file action entry */ if (fae->fae_action == FAE_OPEN || fae->fae_action == FAE_CHDIR) free(fae->fae_path); free(fae); } free(*fa); return (0); } int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t * __restrict fa, int fildes, const char * __restrict path, int oflag, mode_t mode) { posix_spawn_file_actions_entry_t *fae; int error; if (fildes < 0) return (EBADF); /* Allocate object */ fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); if (fae == NULL) return (errno); /* Set values and store in queue */ fae->fae_action = FAE_OPEN; fae->fae_path = strdup(path); if (fae->fae_path == NULL) { error = errno; free(fae); return (error); } fae->fae_fildes = fildes; fae->fae_oflag = oflag; fae->fae_mode = mode; STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); return (0); } int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *fa, int fildes, int newfildes) { posix_spawn_file_actions_entry_t *fae; if (fildes < 0 || newfildes < 0) return (EBADF); /* Allocate object */ fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); if (fae == NULL) return (errno); /* Set values and store in queue */ fae->fae_action = FAE_DUP2; fae->fae_fildes = fildes; fae->fae_newfildes = newfildes; STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); return (0); } int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *fa, int fildes) { posix_spawn_file_actions_entry_t *fae; if (fildes < 0) return (EBADF); /* Allocate object */ fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); if (fae == NULL) return (errno); /* Set values and store in queue */ fae->fae_action = FAE_CLOSE; fae->fae_fildes = fildes; STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); return (0); } int posix_spawn_file_actions_addchdir_np(posix_spawn_file_actions_t * __restrict fa, const char *__restrict path) { posix_spawn_file_actions_entry_t *fae; int error; fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); if (fae == NULL) return (errno); fae->fae_action = FAE_CHDIR; fae->fae_path = strdup(path); if (fae->fae_path == NULL) { error = errno; free(fae); return (error); } STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); return (0); } int posix_spawn_file_actions_addfchdir_np(posix_spawn_file_actions_t *__restrict fa, int fildes) { posix_spawn_file_actions_entry_t *fae; if (fildes < 0) return (EBADF); /* Allocate object */ fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); if (fae == NULL) return (errno); fae->fae_action = FAE_FCHDIR; fae->fae_fildes = fildes; STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); return (0); } int posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t * __restrict fa, int from) { posix_spawn_file_actions_entry_t *fae; if (from < 0) return (EBADF); /* Allocate object */ fae = malloc(sizeof(posix_spawn_file_actions_entry_t)); if (fae == NULL) return (errno); fae->fae_action = FAE_CLOSEFROM; fae->fae_fildes = from; STAILQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list); return (0); } /* * Spawn attributes */ int posix_spawnattr_init(posix_spawnattr_t *ret) { posix_spawnattr_t sa; sa = calloc(1, sizeof(struct __posix_spawnattr)); if (sa == NULL) return (errno); /* Set defaults as specified by POSIX, cleared above */ *ret = sa; return (0); } int posix_spawnattr_destroy(posix_spawnattr_t *sa) { free(*sa); return (0); } int posix_spawnattr_getflags(const posix_spawnattr_t * __restrict sa, short * __restrict flags) { *flags = (*sa)->sa_flags; return (0); } int posix_spawnattr_getpgroup(const posix_spawnattr_t * __restrict sa, pid_t * __restrict pgroup) { *pgroup = (*sa)->sa_pgroup; return (0); } int posix_spawnattr_getschedparam(const posix_spawnattr_t * __restrict sa, struct sched_param * __restrict schedparam) { *schedparam = (*sa)->sa_schedparam; return (0); } int posix_spawnattr_getschedpolicy(const posix_spawnattr_t * __restrict sa, int * __restrict schedpolicy) { *schedpolicy = (*sa)->sa_schedpolicy; return (0); } int posix_spawnattr_getsigdefault(const posix_spawnattr_t * __restrict sa, sigset_t * __restrict sigdefault) { *sigdefault = (*sa)->sa_sigdefault; return (0); } int posix_spawnattr_getsigmask(const posix_spawnattr_t * __restrict sa, sigset_t * __restrict sigmask) { *sigmask = (*sa)->sa_sigmask; return (0); } int posix_spawnattr_setflags(posix_spawnattr_t *sa, short flags) { (*sa)->sa_flags = flags; return (0); } int posix_spawnattr_setpgroup(posix_spawnattr_t *sa, pid_t pgroup) { (*sa)->sa_pgroup = pgroup; return (0); } int posix_spawnattr_setschedparam(posix_spawnattr_t * __restrict sa, const struct sched_param * __restrict schedparam) { (*sa)->sa_schedparam = *schedparam; return (0); } int posix_spawnattr_setschedpolicy(posix_spawnattr_t *sa, int schedpolicy) { (*sa)->sa_schedpolicy = schedpolicy; return (0); } int posix_spawnattr_setsigdefault(posix_spawnattr_t * __restrict sa, const sigset_t * __restrict sigdefault) { (*sa)->sa_sigdefault = *sigdefault; return (0); } int posix_spawnattr_setsigmask(posix_spawnattr_t * __restrict sa, const sigset_t * __restrict sigmask) { (*sa)->sa_sigmask = *sigmask; return (0); } diff --git a/lib/libc/gen/tls.c b/lib/libc/gen/tls.c index 5ceaf439d839..c0ed0126db6a 100644 --- a/lib/libc/gen/tls.c +++ b/lib/libc/gen/tls.c @@ -1,455 +1,453 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2004 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Define stubs for TLS internals so that programs and libraries can * link. These functions will be replaced by functional versions at * runtime from ld-elf.so.1. */ #include #include #include #include #include #include #include "rtld.h" #include "libc_private.h" #define tls_assert(cond) ((cond) ? (void) 0 : \ (tls_msg(#cond ": assert failed: " __FILE__ ":" \ __XSTRING(__LINE__) "\n"), abort())) #define tls_msg(s) write(STDOUT_FILENO, s, strlen(s)) /* Provided by jemalloc to avoid bootstrapping issues. */ void *__je_bootstrap_malloc(size_t size); void *__je_bootstrap_calloc(size_t num, size_t size); void __je_bootstrap_free(void *ptr); __weak_reference(__libc_allocate_tls, _rtld_allocate_tls); __weak_reference(__libc_free_tls, _rtld_free_tls); #ifdef __i386__ __weak_reference(___libc_tls_get_addr, ___tls_get_addr); __attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *); #endif void * __libc_tls_get_addr(void *); __weak_reference(__libc_tls_get_addr, __tls_get_addr); void *_rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign); void _rtld_free_tls(void *tls, size_t tcbsize, size_t tcbalign); void *__libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign); void __libc_free_tls(void *tls, size_t tcbsize, size_t tcbalign); #ifndef PIC static size_t libc_tls_static_space; static size_t libc_tls_init_size; static size_t libc_tls_init_align; static void *libc_tls_init; #endif void * __libc_tls_get_addr(void *vti) { uintptr_t *dtv; tls_index *ti; dtv = _tcb_get()->tcb_dtv; ti = vti; return ((char *)(dtv[ti->ti_module + 1] + ti->ti_offset) + TLS_DTV_OFFSET); } #ifdef __i386__ /* GNU ABI */ __attribute__((__regparm__(1))) void * ___libc_tls_get_addr(void *vti) { return (__libc_tls_get_addr(vti)); } #endif #ifndef PIC static void * libc_malloc_aligned(size_t size, size_t align) { void *mem, *res; if (align < sizeof(void *)) align = sizeof(void *); mem = __je_bootstrap_malloc(size + sizeof(void *) + align - 1); res = (void *)roundup2((uintptr_t)mem + sizeof(void *), align); *(void **)((uintptr_t)res - sizeof(void *)) = mem; return (res); } static void libc_free_aligned(void *ptr) { void *mem; uintptr_t x; if (ptr == NULL) return; x = (uintptr_t)ptr; x -= sizeof(void *); mem = *(void **)x; __je_bootstrap_free(mem); } #ifdef TLS_VARIANT_I /* * There are two versions of variant I of TLS * * - ARM and aarch64 uses original variant I as is described in [1] and [2], * where TP points to start of TCB followed by aligned TLS segment. * Both TCB and TLS must be aligned to alignment of TLS section. The TCB[0] * points to DTV vector and DTV values are real addresses (without bias). * Note: for Local Exec TLS Model, the offsets from TP (TCB in this case) to * TLS variables are computed by linker, so we cannot overalign TLS section. * * - PowerPC and RISC-V use modified version of variant I, described in [3] * where TP points (with bias) to TLS and TCB immediately precedes TLS without * any alignment gap[4]. Only TLS should be aligned. The TCB[0] points to DTV * vector and DTV values are biased by constant value (TLS_DTV_OFFSET) from * real addresses[5]. * * [1] Ulrich Drepper: ELF Handling for Thread-Local Storage * www.akkadia.org/drepper/tls.pdf * * [2] ARM IHI 0045E: Addenda to, and Errata in, the ABI for the ARM(r) * Architecture * infocenter.arm.com/help/topic/com.arm.doc.ihi0045e/IHI0045E_ABI_addenda.pdf * * [3] OpenPOWER: Power Architecture 64-Bit ELF V2 ABI Specification * https://members.openpowerfoundation.org/document/dl/576 * * [4] Its unclear if "without any alignment gap" is hard ABI requirement, * but we must follow this rule due to suboptimal _tcb_set() * (aka _SET_TP) implementation. This function doesn't expect TP but * TCB as argument. * * [5] I'm not able to validate "values are biased" assertions. */ /* * Return pointer to allocated TLS block */ static void * get_tls_block_ptr(void *tcb, size_t tcbsize) { size_t extra_size, post_size, pre_size, tls_block_size; /* Compute fragments sizes. */ extra_size = tcbsize - TLS_TCB_SIZE; #if defined(__aarch64__) || defined(__arm__) post_size = roundup2(TLS_TCB_SIZE, libc_tls_init_align) - TLS_TCB_SIZE; #else post_size = 0; #endif tls_block_size = tcbsize + post_size; pre_size = roundup2(tls_block_size, libc_tls_init_align) - tls_block_size; return ((char *)tcb - pre_size - extra_size); } /* * Free Static TLS using the Variant I method. The tcbsize * and tcbalign parameters must be the same as those used to allocate * the block. */ void __libc_free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused) { Elf_Addr *dtv; Elf_Addr **tls; tls = (Elf_Addr **)tcb; dtv = tls[0]; __je_bootstrap_free(dtv); libc_free_aligned(get_tls_block_ptr(tcb, tcbsize)); } /* * Allocate Static TLS using the Variant I method. * * To handle all above requirements, we setup the following layout for * TLS block: * (whole memory block is aligned with MAX(TLS_TCB_ALIGN, tls_init_align)) * * +----------+--------------+--------------+-----------+------------------+ * | pre gap | extended TCB | TCB | post gap | TLS segment | * | pre_size | extra_size | TLS_TCB_SIZE | post_size | tls_static_space | * +----------+--------------+--------------+-----------+------------------+ * * where: * extra_size is tcbsize - TLS_TCB_SIZE * post_size is used to adjust TCB to TLS alignment for first version of TLS * layout and is always 0 for second version. * pre_size is used to adjust TCB alignment for first version and to adjust * TLS alignment for second version. * */ void * __libc_allocate_tls(void *oldtcb, size_t tcbsize, size_t tcbalign) { Elf_Addr *dtv, **tcb; char *tls_block, *tls; size_t extra_size, maxalign, post_size, pre_size, tls_block_size; if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE) return (oldtcb); tls_assert(tcbalign >= TLS_TCB_ALIGN); maxalign = MAX(tcbalign, libc_tls_init_align); /* Compute fragmets sizes. */ extra_size = tcbsize - TLS_TCB_SIZE; #if defined(__aarch64__) || defined(__arm__) post_size = roundup2(TLS_TCB_SIZE, libc_tls_init_align) - TLS_TCB_SIZE; #else post_size = 0; #endif tls_block_size = tcbsize + post_size; pre_size = roundup2(tls_block_size, libc_tls_init_align) - tls_block_size; tls_block_size += pre_size + libc_tls_static_space; /* Allocate whole TLS block */ tls_block = libc_malloc_aligned(tls_block_size, maxalign); if (tls_block == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } memset(tls_block, 0, tls_block_size); tcb = (Elf_Addr **)(tls_block + pre_size + extra_size); tls = (char *)tcb + TLS_TCB_SIZE + post_size; if (oldtcb != NULL) { memcpy(tls_block, get_tls_block_ptr(oldtcb, tcbsize), tls_block_size); libc_free_aligned(oldtcb); /* Adjust the DTV. */ dtv = tcb[0]; dtv[2] = (Elf_Addr)(tls + TLS_DTV_OFFSET); } else { dtv = __je_bootstrap_malloc(3 * sizeof(Elf_Addr)); if (dtv == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } /* Build the DTV. */ tcb[0] = dtv; dtv[0] = 1; /* Generation. */ dtv[1] = 1; /* Segments count. */ dtv[2] = (Elf_Addr)(tls + TLS_DTV_OFFSET); if (libc_tls_init_size > 0) memcpy(tls, libc_tls_init, libc_tls_init_size); } return (tcb); } #endif #ifdef TLS_VARIANT_II /* * Free Static TLS using the Variant II method. */ void __libc_free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign) { size_t size; Elf_Addr* dtv; Elf_Addr tlsstart, tlsend; /* * Figure out the size of the initial TLS block so that we can * find stuff which ___tls_get_addr() allocated dynamically. */ tcbalign = MAX(tcbalign, libc_tls_init_align); size = roundup2(libc_tls_static_space, tcbalign); dtv = ((Elf_Addr**)tcb)[1]; tlsend = (Elf_Addr) tcb; tlsstart = tlsend - size; libc_free_aligned((void*)tlsstart); __je_bootstrap_free(dtv); } /* * Allocate Static TLS using the Variant II method. */ void * __libc_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) { size_t size; char *tls; Elf_Addr *dtv; Elf_Addr segbase, oldsegbase; tcbalign = MAX(tcbalign, libc_tls_init_align); size = roundup2(libc_tls_static_space, tcbalign); if (tcbsize < 2 * sizeof(Elf_Addr)) tcbsize = 2 * sizeof(Elf_Addr); tls = libc_malloc_aligned(size + tcbsize, tcbalign); if (tls == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } memset(tls, 0, size + tcbsize); dtv = __je_bootstrap_malloc(3 * sizeof(Elf_Addr)); if (dtv == NULL) { tls_msg("__libc_allocate_tls: Out of memory.\n"); abort(); } segbase = (Elf_Addr)(tls + size); ((Elf_Addr*)segbase)[0] = segbase; ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv; dtv[0] = 1; dtv[1] = 1; dtv[2] = segbase - libc_tls_static_space; if (oldtls) { /* * Copy the static TLS block over whole. */ oldsegbase = (Elf_Addr) oldtls; memcpy((void *)(segbase - libc_tls_static_space), (const void *)(oldsegbase - libc_tls_static_space), libc_tls_static_space); /* * We assume that this block was the one we created with * allocate_initial_tls(). */ _rtld_free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr)); } else { memcpy((void *)(segbase - libc_tls_static_space), libc_tls_init, libc_tls_init_size); memset((void *)(segbase - libc_tls_static_space + libc_tls_init_size), 0, libc_tls_static_space - libc_tls_init_size); } return (void*) segbase; } #endif /* TLS_VARIANT_II */ #else void * __libc_allocate_tls(void *oldtls __unused, size_t tcbsize __unused, size_t tcbalign __unused) { return (0); } void __libc_free_tls(void *tcb __unused, size_t tcbsize __unused, size_t tcbalign __unused) { } #endif /* PIC */ -extern char **environ; - void _init_tls(void) { #ifndef PIC Elf_Addr *sp; Elf_Auxinfo *aux, *auxp; Elf_Phdr *phdr; size_t phent, phnum; int i; void *tls; sp = (Elf_Addr *) environ; while (*sp++ != 0) ; aux = (Elf_Auxinfo *) sp; phdr = NULL; phent = phnum = 0; for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { switch (auxp->a_type) { case AT_PHDR: phdr = auxp->a_un.a_ptr; break; case AT_PHENT: phent = auxp->a_un.a_val; break; case AT_PHNUM: phnum = auxp->a_un.a_val; break; } } if (phdr == NULL || phent != sizeof(Elf_Phdr) || phnum == 0) return; for (i = 0; (unsigned) i < phnum; i++) { if (phdr[i].p_type == PT_TLS) { libc_tls_static_space = roundup2(phdr[i].p_memsz, phdr[i].p_align); libc_tls_init_size = phdr[i].p_filesz; libc_tls_init_align = phdr[i].p_align; libc_tls_init = (void *)phdr[i].p_vaddr; break; } } tls = _rtld_allocate_tls(NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN); _tcb_set(tls); #endif } diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 67750d354f22..228eb5c0945f 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -1,450 +1,452 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1998 John Birrell . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ * * Private definitions for libc, libc_r and libpthread. * */ #ifndef _LIBC_PRIVATE_H_ #define _LIBC_PRIVATE_H_ #include #include +extern char **environ; + /* * The kernel doesn't expose PID_MAX to the user space. Save it here * to allow to run a newer world on a pre-1400079 kernel. */ #define _PID_MAX 99999 /* * This global flag is non-zero when a process has created one * or more threads. It is used to avoid calling locking functions * when they are not required. */ #ifndef __LIBC_ISTHREADED_DECLARED #define __LIBC_ISTHREADED_DECLARED extern int __isthreaded; #endif /* * Elf_Auxinfo *__elf_aux_vector, the pointer to the ELF aux vector * provided by kernel. Either set for us by rtld, or found at runtime * on stack for static binaries. * * Type is void to avoid polluting whole libc with ELF types. */ extern void *__elf_aux_vector; /* * libc should use libc_dlopen internally, which respects a global * flag where loading of new shared objects can be restricted. */ void *libc_dlopen(const char *, int); /* * For dynamic linker. */ void _rtld_error(const char *fmt, ...); /* * File lock contention is difficult to diagnose without knowing * where locks were set. Allow a debug library to be built which * records the source file and line number of each lock call. */ #ifdef _FLOCK_DEBUG #define _FLOCKFILE(x) _flockfile_debug(x, __FILE__, __LINE__) #else #define _FLOCKFILE(x) _flockfile(x) #endif /* * Macros for locking and unlocking FILEs. These test if the * process is threaded to avoid locking when not required. */ #define FLOCKFILE(fp) if (__isthreaded) _FLOCKFILE(fp) #define FUNLOCKFILE(fp) if (__isthreaded) _funlockfile(fp) struct _spinlock; extern struct _spinlock __stdio_thread_lock __hidden; #define STDIO_THREAD_LOCK() \ do { \ if (__isthreaded) \ _SPINLOCK(&__stdio_thread_lock); \ } while (0) #define STDIO_THREAD_UNLOCK() \ do { \ if (__isthreaded) \ _SPINUNLOCK(&__stdio_thread_lock); \ } while (0) void __libc_spinlock_stub(struct _spinlock *); void __libc_spinunlock_stub(struct _spinlock *); /* * Indexes into the pthread jump table. * * Warning! If you change this type, you must also change the threads * libraries that reference it (libc_r, libpthread). */ typedef enum { PJT_ATFORK, PJT_ATTR_DESTROY, PJT_ATTR_GETDETACHSTATE, PJT_ATTR_GETGUARDSIZE, PJT_ATTR_GETINHERITSCHED, PJT_ATTR_GETSCHEDPARAM, PJT_ATTR_GETSCHEDPOLICY, PJT_ATTR_GETSCOPE, PJT_ATTR_GETSTACKADDR, PJT_ATTR_GETSTACKSIZE, PJT_ATTR_INIT, PJT_ATTR_SETDETACHSTATE, PJT_ATTR_SETGUARDSIZE, PJT_ATTR_SETINHERITSCHED, PJT_ATTR_SETSCHEDPARAM, PJT_ATTR_SETSCHEDPOLICY, PJT_ATTR_SETSCOPE, PJT_ATTR_SETSTACKADDR, PJT_ATTR_SETSTACKSIZE, PJT_CANCEL, PJT_CLEANUP_POP, PJT_CLEANUP_PUSH, PJT_COND_BROADCAST, PJT_COND_DESTROY, PJT_COND_INIT, PJT_COND_SIGNAL, PJT_COND_TIMEDWAIT, PJT_COND_WAIT, PJT_DETACH, PJT_EQUAL, PJT_EXIT, PJT_GETSPECIFIC, PJT_JOIN, PJT_KEY_CREATE, PJT_KEY_DELETE, PJT_KILL, PJT_MAIN_NP, PJT_MUTEXATTR_DESTROY, PJT_MUTEXATTR_INIT, PJT_MUTEXATTR_SETTYPE, PJT_MUTEX_DESTROY, PJT_MUTEX_INIT, PJT_MUTEX_LOCK, PJT_MUTEX_TRYLOCK, PJT_MUTEX_UNLOCK, PJT_ONCE, PJT_RWLOCK_DESTROY, PJT_RWLOCK_INIT, PJT_RWLOCK_RDLOCK, PJT_RWLOCK_TRYRDLOCK, PJT_RWLOCK_TRYWRLOCK, PJT_RWLOCK_UNLOCK, PJT_RWLOCK_WRLOCK, PJT_SELF, PJT_SETCANCELSTATE, PJT_SETCANCELTYPE, PJT_SETSPECIFIC, PJT_SIGMASK, PJT_TESTCANCEL, PJT_CLEANUP_POP_IMP, PJT_CLEANUP_PUSH_IMP, PJT_CANCEL_ENTER, PJT_CANCEL_LEAVE, PJT_MUTEX_CONSISTENT, PJT_MUTEXATTR_GETROBUST, PJT_MUTEXATTR_SETROBUST, PJT_GETTHREADID_NP, PJT_ATTR_GET_NP, PJT_MAX } pjt_index_t; typedef int (*pthread_func_t)(void); typedef pthread_func_t pthread_func_entry_t[2]; extern pthread_func_entry_t __thr_jtable[]; void __set_error_selector(int *(*arg)(void)); int _pthread_mutex_init_calloc_cb_stub(pthread_mutex_t *mutex, void *(calloc_cb)(__size_t, __size_t)); typedef int (*interpos_func_t)(void); interpos_func_t *__libc_interposing_slot(int interposno); extern interpos_func_t __libc_interposing[] __hidden; enum { INTERPOS_accept, INTERPOS_accept4, INTERPOS_aio_suspend, INTERPOS_close, INTERPOS_connect, INTERPOS_fcntl, INTERPOS_fsync, INTERPOS_fork, INTERPOS_msync, INTERPOS_nanosleep, INTERPOS_openat, INTERPOS_poll, INTERPOS_pselect, INTERPOS_recvfrom, INTERPOS_recvmsg, INTERPOS_select, INTERPOS_sendmsg, INTERPOS_sendto, INTERPOS_setcontext, INTERPOS_sigaction, INTERPOS_sigprocmask, INTERPOS_sigsuspend, INTERPOS_sigwait, INTERPOS_sigtimedwait, INTERPOS_sigwaitinfo, INTERPOS_swapcontext, INTERPOS_system, INTERPOS_tcdrain, INTERPOS_read, INTERPOS_readv, INTERPOS_wait4, INTERPOS_write, INTERPOS_writev, INTERPOS__pthread_mutex_init_calloc_cb, INTERPOS_spinlock, INTERPOS_spinunlock, INTERPOS_kevent, INTERPOS_wait6, INTERPOS_ppoll, INTERPOS_map_stacks_exec, INTERPOS_fdatasync, INTERPOS_clock_nanosleep, INTERPOS_distribute_static_tls, INTERPOS_pdfork, INTERPOS_MAX }; /* * yplib internal interfaces */ #ifdef YP int _yp_check(char **); #endif void __libc_start1(int, char *[], char *[], void (*)(void), int (*)(int, char *[], char *[])) __dead2; void __libc_start1_gcrt(int, char *[], char *[], void (*)(void), int (*)(int, char *[], char *[]), int *, int *) __dead2; /* * Initialise TLS for static programs */ void _init_tls(void); /* * Provides pthread_once()-like functionality for both single-threaded * and multi-threaded applications. */ int _once(pthread_once_t *, void (*)(void)); /* * This is a pointer in the C run-time startup code. It is used * by getprogname() and setprogname(). */ extern const char *__progname; /* * This function is used by the threading libraries to notify malloc that a * thread is exiting. */ void _malloc_thread_cleanup(void); /* * This function is used by the threading libraries to notify libc that a * thread is exiting, so its thread-local dtors should be called. */ void __cxa_thread_call_dtors(void); int __cxa_thread_atexit_hidden(void (*dtor_func)(void *), void *obj, void *dso_symbol) __hidden; /* * These functions are used by the threading libraries in order to protect * malloc across fork(). */ void _malloc_prefork(void); void _malloc_postfork(void); void _malloc_first_thread(void); /* * Function to clean up streams, called from abort() and exit(). */ extern void (*__cleanup)(void) __hidden; /* * Get kern.osreldate to detect ABI revisions. Explicitly * ignores value of $OSVERSION and caches result. */ int __getosreldate(void); #include #include struct aiocb; struct fd_set; struct iovec; struct kevent; struct msghdr; struct pollfd; struct rusage; struct sigaction; struct sockaddr; struct stat; struct statfs; struct timespec; struct timeval; struct timezone; struct __siginfo; struct __ucontext; struct __wrusage; enum idtype; int __sys_aio_suspend(const struct aiocb * const[], int, const struct timespec *); int __sys_accept(int, struct sockaddr *, __socklen_t *); int __sys_accept4(int, struct sockaddr *, __socklen_t *, int); int __sys_clock_gettime(__clockid_t, struct timespec *ts); int __sys_clock_nanosleep(__clockid_t, int, const struct timespec *, struct timespec *); int __sys_close(int); int __sys_close_range(unsigned, unsigned, int); int __sys_connect(int, const struct sockaddr *, __socklen_t); int __sys_fcntl(int, int, ...); int __sys_fdatasync(int); int __sys_fstat(int fd, struct stat *); int __sys_fstatfs(int fd, struct statfs *); int __sys_fstatat(int, const char *, struct stat *, int); int __sys_fsync(int); __pid_t __sys_fork(void); int __sys_ftruncate(int, __off_t); __ssize_t __sys_getdirentries(int, char *, __size_t, __off_t *); int __sys_getfsstat(struct statfs *, long, int); int __sys_gettimeofday(struct timeval *, struct timezone *); int __sys_kevent(int, const struct kevent *, int, struct kevent *, int, const struct timespec *); __off_t __sys_lseek(int, __off_t, int); void *__sys_mmap(void *, __size_t, int, int, int, __off_t); int __sys_msync(void *, __size_t, int); int __sys_nanosleep(const struct timespec *, struct timespec *); int __sys_open(const char *, int, ...); int __sys_openat(int, const char *, int, ...); int __sys_pdfork(int *, int); int __sys_pselect(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timespec *, const __sigset_t *); int __sys_ptrace(int, __pid_t, char *, int); int __sys_poll(struct pollfd *, unsigned, int); int __sys_ppoll(struct pollfd *, unsigned, const struct timespec *, const __sigset_t *); __ssize_t __sys_pread(int, void *, __size_t, __off_t); __ssize_t __sys_pwrite(int, const void *, __size_t, __off_t); __ssize_t __sys_read(int, void *, __size_t); __ssize_t __sys_readv(int, const struct iovec *, int); __ssize_t __sys_recv(int, void *, __size_t, int); __ssize_t __sys_recvfrom(int, void *, __size_t, int, struct sockaddr *, __socklen_t *); __ssize_t __sys_recvmsg(int, struct msghdr *, int); int __sys_sched_getcpu(void); int __sys_select(int, struct fd_set *, struct fd_set *, struct fd_set *, struct timeval *); __ssize_t __sys_sendmsg(int, const struct msghdr *, int); __ssize_t __sys_sendto(int, const void *, __size_t, int, const struct sockaddr *, __socklen_t); int __sys_setcontext(const struct __ucontext *); int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigprocmask(int, const __sigset_t *, __sigset_t *); int __sys_sigsuspend(const __sigset_t *); int __sys_sigtimedwait(const __sigset_t *, struct __siginfo *, const struct timespec *); int __sys_sigwait(const __sigset_t *, int *); int __sys_sigwaitinfo(const __sigset_t *, struct __siginfo *); int __sys___specialfd(int, const void *, __size_t); int __sys_statfs(const char *, struct statfs *); int __sys_swapcontext(struct __ucontext *, const struct __ucontext *); int __sys_thr_kill(long, int); int __sys_thr_self(long *); int __sys_truncate(const char *, __off_t); __pid_t __sys_wait4(__pid_t, int *, int, struct rusage *); __pid_t __sys_wait6(enum idtype, __id_t, int *, int, struct __wrusage *, struct __siginfo *); __ssize_t __sys_write(int, const void *, __size_t); __ssize_t __sys_writev(int, const struct iovec *, int); int __sys_shm_open2(const char *, int, __mode_t, int, const char *); int __libc_sigaction(int, const struct sigaction *, struct sigaction *) __hidden; int __libc_sigprocmask(int, const __sigset_t *, __sigset_t *) __hidden; int __libc_sigsuspend(const __sigset_t *) __hidden; int __libc_sigwait(const __sigset_t * __restrict, int * restrict sig); int __libc_system(const char *); int __libc_tcdrain(int); int __fcntl_compat(int fd, int cmd, ...); int __sys_futimens(int fd, const struct timespec *times) __hidden; int __sys_utimensat(int fd, const char *path, const struct timespec *times, int flag) __hidden; /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); int _elf_aux_info(int aux, void *buf, int buflen); struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __init_elf_aux_vector(void); void __libc_map_stacks_exec(void); void __libc_distribute_static_tls(__size_t, void *, __size_t, __size_t); __uintptr_t __libc_static_tls_base(__size_t); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); struct _pthread_cleanup_info; void ___pthread_cleanup_push_imp(void (*)(void *), void *, struct _pthread_cleanup_info *); void ___pthread_cleanup_pop_imp(int); void __throw_constraint_handler_s(const char * restrict msg, int error); struct __nl_cat_d; struct _xlocale; struct __nl_cat_d *__catopen_l(const char *name, int type, struct _xlocale *locale); #endif /* _LIBC_PRIVATE_H_ */ diff --git a/lib/libc/stdlib/getenv.c b/lib/libc/stdlib/getenv.c index ed0c9efcba6d..7ca27ab710c4 100644 --- a/lib/libc/stdlib/getenv.c +++ b/lib/libc/stdlib/getenv.c @@ -1,718 +1,717 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2007-2009 Sean C. Farley * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include "un-namespace.h" - +#include "libc_private.h" static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find "; static const char CorruptEnvValueMsg[] = "environment corrupt; missing value for "; /* * Standard environ. environ variable is exposed to entire process. * * origEnviron: Upon cleanup on unloading of library or failure, this * allows environ to return to as it was before. * environSize: Number of variables environ can hold. Can only * increase. * intEnviron: Internally-built environ. Exposed via environ during * (re)builds of the environment. */ -extern char **environ; static char **origEnviron; static char **intEnviron = NULL; static int environSize = 0; /* * Array of environment variables built from environ. Each element records: * name: Pointer to name=value string * name length: Length of name not counting '=' character * value: Pointer to value within same string as name * value size: Size (not length) of space for value not counting the * nul character * active state: true/false value to signify whether variable is active. * Useful since multiple variables with the same name can * co-exist. At most, one variable can be active at any * one time. * putenv: Created from putenv() call. This memory must not be * reused. */ static struct envVars { size_t nameLen; size_t valueSize; char *name; char *value; bool active; bool putenv; } *envVars = NULL; /* * Environment array information. * * envActive: Number of active variables in array. * envVarsSize: Size of array. * envVarsTotal: Number of total variables in array (active or not). */ static int envActive = 0; static int envVarsSize = 0; static int envVarsTotal = 0; /* Deinitialization of new environment. */ static void __attribute__ ((destructor)) __clean_env_destructor(void); /* * A simple version of warnx() to avoid the bloat of including stdio in static * binaries. */ static void __env_warnx(const char *msg, const char *name, size_t nameLen) { static const char nl[] = "\n"; static const char progSep[] = ": "; _write(STDERR_FILENO, _getprogname(), strlen(_getprogname())); _write(STDERR_FILENO, progSep, sizeof(progSep) - 1); _write(STDERR_FILENO, msg, strlen(msg)); _write(STDERR_FILENO, name, nameLen); _write(STDERR_FILENO, nl, sizeof(nl) - 1); return; } /* * Inline strlen() for performance. Also, perform check for an equals sign. * Cheaper here than performing a strchr() later. */ static inline size_t __strleneq(const char *str) { const char *s; for (s = str; *s != '\0'; ++s) if (*s == '=') return (0); return (s - str); } /* * Comparison of an environment name=value to a name. */ static inline bool strncmpeq(const char *nameValue, const char *name, size_t nameLen) { if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=') return (true); return (false); } /* * Using environment, returns pointer to value associated with name, if any, * else NULL. If the onlyActive flag is set to true, only variables that are * active are returned else all are. */ static inline char * __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive) { int ndx; /* * Find environment variable from end of array (more likely to be * active). A variable created by putenv is always active, or it is not * tracked in the array. */ for (ndx = *envNdx; ndx >= 0; ndx--) if (envVars[ndx].putenv) { if (strncmpeq(envVars[ndx].name, name, nameLen)) { *envNdx = ndx; return (envVars[ndx].name + nameLen + sizeof ("=") - 1); } } else if ((!onlyActive || envVars[ndx].active) && (envVars[ndx].nameLen == nameLen && strncmpeq(envVars[ndx].name, name, nameLen))) { *envNdx = ndx; return (envVars[ndx].value); } return (NULL); } /* * Using environ, returns pointer to value associated with name, if any, else * NULL. Used on the original environ passed into the program. */ static char * __findenv_environ(const char *name, size_t nameLen) { int envNdx; /* Find variable within environ. */ for (envNdx = 0; environ[envNdx] != NULL; envNdx++) if (strncmpeq(environ[envNdx], name, nameLen)) return (&(environ[envNdx][nameLen + sizeof("=") - 1])); return (NULL); } /* * Remove variable added by putenv() from variable tracking array. */ static void __remove_putenv(int envNdx) { envVarsTotal--; if (envVarsTotal > envNdx) memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]), (envVarsTotal - envNdx) * sizeof (*envVars)); memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars)); return; } /* * Deallocate the environment built from environ as well as environ then set * both to NULL. Eases debugging of memory leaks. */ static void __clean_env(bool freeVars) { int envNdx; /* Deallocate environment and environ if created by *env(). */ if (envVars != NULL) { for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) /* Free variables or deactivate them. */ if (envVars[envNdx].putenv) { if (!freeVars) __remove_putenv(envNdx); } else { if (freeVars) free(envVars[envNdx].name); else envVars[envNdx].active = false; } if (freeVars) { free(envVars); envVars = NULL; } else envActive = 0; /* Restore original environ if it has not updated by program. */ if (origEnviron != NULL) { if (environ == intEnviron) environ = origEnviron; free(intEnviron); intEnviron = NULL; environSize = 0; } } return; } /* * Using the environment, rebuild the environ array for use by other C library * calls that depend upon it. */ static int __rebuild_environ(int newEnvironSize) { char **tmpEnviron; int envNdx; int environNdx; int tmpEnvironSize; /* Resize environ. */ if (newEnvironSize > environSize) { tmpEnvironSize = newEnvironSize * 2; tmpEnviron = reallocarray(intEnviron, tmpEnvironSize + 1, sizeof(*intEnviron)); if (tmpEnviron == NULL) return (-1); environSize = tmpEnvironSize; intEnviron = tmpEnviron; } envActive = newEnvironSize; /* Assign active variables to environ. */ for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--) if (envVars[envNdx].active) intEnviron[environNdx++] = envVars[envNdx].name; intEnviron[environNdx] = NULL; /* Always set environ which may have been replaced by program. */ environ = intEnviron; return (0); } /* * Enlarge new environment. */ static inline bool __enlarge_env(void) { int newEnvVarsSize; struct envVars *tmpEnvVars; envVarsTotal++; if (envVarsTotal > envVarsSize) { newEnvVarsSize = envVarsTotal * 2; tmpEnvVars = reallocarray(envVars, newEnvVarsSize, sizeof(*envVars)); if (tmpEnvVars == NULL) { envVarsTotal--; return (false); } envVarsSize = newEnvVarsSize; envVars = tmpEnvVars; } return (true); } /* * Using environ, build an environment for use by standard C library calls. */ static int __build_env(void) { char **env; int activeNdx; int envNdx; int savedErrno; size_t nameLen; /* Check for non-existant environment. */ if (environ == NULL || environ[0] == NULL) return (0); /* Count environment variables. */ for (env = environ, envVarsTotal = 0; *env != NULL; env++) envVarsTotal++; envVarsSize = envVarsTotal * 2; /* Create new environment. */ envVars = calloc(envVarsSize, sizeof(*envVars)); if (envVars == NULL) goto Failure; /* Copy environ values and keep track of them. */ for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) { envVars[envNdx].putenv = false; envVars[envNdx].name = strdup(environ[envVarsTotal - envNdx - 1]); if (envVars[envNdx].name == NULL) goto Failure; envVars[envNdx].value = strchr(envVars[envNdx].name, '='); if (envVars[envNdx].value != NULL) { envVars[envNdx].value++; envVars[envNdx].valueSize = strlen(envVars[envNdx].value); } else { __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name, strlen(envVars[envNdx].name)); errno = EFAULT; goto Failure; } /* * Find most current version of variable to make active. This * will prevent multiple active variables from being created * during this initialization phase. */ nameLen = envVars[envNdx].value - envVars[envNdx].name - 1; envVars[envNdx].nameLen = nameLen; activeNdx = envVarsTotal - 1; if (__findenv(envVars[envNdx].name, nameLen, &activeNdx, false) == NULL) { __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name, nameLen); errno = EFAULT; goto Failure; } envVars[activeNdx].active = true; } /* Create a new environ. */ origEnviron = environ; environ = NULL; if (__rebuild_environ(envVarsTotal) == 0) return (0); Failure: savedErrno = errno; __clean_env(true); errno = savedErrno; return (-1); } /* * Destructor function with default argument to __clean_env(). */ static void __clean_env_destructor(void) { __clean_env(true); return; } /* * Returns the value of a variable or NULL if none are found. */ char * getenv(const char *name) { int envNdx; size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (NULL); } /* * Variable search order: * 1. Check for an empty environ. This allows an application to clear * the environment. * 2. Search the external environ array. * 3. Search the internal environment. * * Since malloc() depends upon getenv(), getenv() must never cause the * internal environment storage to be generated. */ if (environ == NULL || environ[0] == NULL) return (NULL); else if (envVars == NULL || environ != intEnviron) return (__findenv_environ(name, nameLen)); else { envNdx = envVarsTotal - 1; return (__findenv(name, nameLen, &envNdx, true)); } } /* * Set the value of a variable. Older settings are labeled as inactive. If an * older setting has enough room to store the new value, it will be reused. No * previous variables are ever freed here to avoid causing a segmentation fault * in a user's code. * * The variables nameLen and valueLen are passed into here to allow the caller * to calculate the length by means besides just strlen(). */ static int __setenv(const char *name, size_t nameLen, const char *value, int overwrite) { bool reuse; char *env; int envNdx; int newEnvActive; size_t valueLen; /* Find existing environment variable large enough to use. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; valueLen = strlen(value); reuse = false; if (__findenv(name, nameLen, &envNdx, false) != NULL) { /* Deactivate entry if overwrite is allowed. */ if (envVars[envNdx].active) { if (overwrite == 0) return (0); envVars[envNdx].active = false; newEnvActive--; } /* putenv() created variable cannot be reused. */ if (envVars[envNdx].putenv) __remove_putenv(envNdx); /* Entry is large enough to reuse. */ else if (envVars[envNdx].valueSize >= valueLen) reuse = true; } /* Create new variable if none was found of sufficient size. */ if (! reuse) { /* Enlarge environment. */ envNdx = envVarsTotal; if (!__enlarge_env()) return (-1); /* Create environment entry. */ envVars[envNdx].name = malloc(nameLen + sizeof ("=") + valueLen); if (envVars[envNdx].name == NULL) { envVarsTotal--; return (-1); } envVars[envNdx].nameLen = nameLen; envVars[envNdx].valueSize = valueLen; /* Save name of name/value pair. */ env = stpncpy(envVars[envNdx].name, name, nameLen); *env++ = '='; } else env = envVars[envNdx].value; /* Save value of name/value pair. */ strcpy(env, value); envVars[envNdx].value = env; envVars[envNdx].active = true; newEnvActive++; /* No need to rebuild environ if an active variable was reused. */ if (reuse && newEnvActive == envActive) return (0); else return (__rebuild_environ(newEnvActive)); } /* * If the program attempts to replace the array of environment variables * (environ) environ or sets the first varible to NULL, then deactivate all * variables and merge in the new list from environ. */ static int __merge_environ(void) { char **env; char *equals; /* * Internally-built environ has been replaced or cleared (detected by * using the count of active variables against a NULL as the first value * in environ). Clean up everything. */ if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 && environ[0] == NULL))) { /* Deactivate all environment variables. */ if (envActive > 0) { origEnviron = NULL; __clean_env(false); } /* * Insert new environ into existing, yet deactivated, * environment array. */ origEnviron = environ; if (origEnviron != NULL) for (env = origEnviron; *env != NULL; env++) { if ((equals = strchr(*env, '=')) == NULL) { __env_warnx(CorruptEnvValueMsg, *env, strlen(*env)); errno = EFAULT; return (-1); } if (__setenv(*env, equals - *env, equals + 1, 1) == -1) return (-1); } } return (0); } /* * The exposed setenv() that performs a few tests before calling the function * (__setenv()) that does the actual work of inserting a variable into the * environment. */ int setenv(const char *name, const char *value, int overwrite) { size_t nameLen; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); return (__setenv(name, nameLen, value, overwrite)); } /* * Insert a "name=value" string into the environment. Special settings must be * made to keep setenv() from reusing this memory block and unsetenv() from * allowing it to be tracked. */ int putenv(char *string) { char *equals; int envNdx; int newEnvActive; size_t nameLen; /* Check for malformed argument. */ if (string == NULL || (equals = strchr(string, '=')) == NULL || (nameLen = equals - string) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate previous environment variable. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; if (__findenv(string, nameLen, &envNdx, true) != NULL) { /* Reuse previous putenv slot. */ if (envVars[envNdx].putenv) { envVars[envNdx].name = string; return (__rebuild_environ(envActive)); } else { newEnvActive--; envVars[envNdx].active = false; } } /* Enlarge environment. */ envNdx = envVarsTotal; if (!__enlarge_env()) return (-1); /* Create environment entry. */ envVars[envNdx].name = string; envVars[envNdx].nameLen = -1; envVars[envNdx].value = NULL; envVars[envNdx].valueSize = -1; envVars[envNdx].putenv = true; envVars[envNdx].active = true; newEnvActive++; return (__rebuild_environ(newEnvActive)); } /* * Unset variable with the same name by flagging it as inactive. No variable is * ever freed. */ int unsetenv(const char *name) { int envNdx; size_t nameLen; int newEnvActive; /* Check for malformed name. */ if (name == NULL || (nameLen = __strleneq(name)) == 0) { errno = EINVAL; return (-1); } /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Deactivate specified variable. */ /* Remove all occurrences. */ envNdx = envVarsTotal - 1; newEnvActive = envActive; while (__findenv(name, nameLen, &envNdx, true) != NULL) { envVars[envNdx].active = false; if (envVars[envNdx].putenv) __remove_putenv(envNdx); envNdx--; newEnvActive--; } if (newEnvActive != envActive) __rebuild_environ(newEnvActive); return (0); } /* * Unset all variable by flagging them as inactive. No variable is * ever freed. */ int clearenv(void) { int ndx; /* Initialize environment. */ if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1)) return (-1); /* Remove from the end to not shuffle memory too much. */ for (ndx = envVarsTotal - 1; ndx >= 0; ndx--) { envVars[ndx].active = false; if (envVars[ndx].putenv) __remove_putenv(ndx); } __rebuild_environ(0); return (0); }