diff options
-rw-r--r-- | linker/Android.mk | 11 | ||||
-rw-r--r-- | linker/debugger.cpp (renamed from linker/debugger.c) | 85 | ||||
-rw-r--r-- | linker/linker.cpp | 73 | ||||
-rw-r--r-- | linker/linker.h | 12 | ||||
-rw-r--r-- | linker/linker_debug.h | 8 | ||||
-rw-r--r-- | linker/linker_environ.c | 202 | ||||
-rw-r--r-- | linker/linker_environ.cpp | 222 | ||||
-rw-r--r-- | linker/linker_environ.h | 41 | ||||
-rw-r--r-- | linker/linker_format.cpp (renamed from linker/linker_format.c) | 53 | ||||
-rw-r--r-- | linker/linker_format.h | 18 | ||||
-rw-r--r-- | linker/linker_phdr.cpp (renamed from linker/linker_phdr.c) | 1 | ||||
-rw-r--r-- | linker/linker_phdr.h | 10 | ||||
-rw-r--r-- | linker/rt.cpp (renamed from linker/rt.c) | 5 |
13 files changed, 336 insertions, 405 deletions
diff --git a/linker/Android.mk b/linker/Android.mk index 503da86..e3ac7e9 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -3,20 +3,19 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ arch/$(TARGET_ARCH)/begin.S \ - debugger.c \ + debugger.cpp \ dlfcn.cpp \ linker.cpp \ - linker_environ.c \ - linker_format.c \ - linker_phdr.c \ - rt.c + linker_environ.cpp \ + linker_format.cpp \ + linker_phdr.cpp \ + rt.cpp LOCAL_LDFLAGS := -shared LOCAL_CFLAGS += -fno-stack-protector \ -Wstrict-overflow=5 \ -fvisibility=hidden \ - -std=gnu99 \ -Wall -Wextra # Set LINKER_DEBUG to either 1 or 0 diff --git a/linker/debugger.c b/linker/debugger.cpp index e4d4ae9..bba89b8 100644 --- a/linker/debugger.c +++ b/linker/debugger.cpp @@ -26,6 +26,9 @@ * SUCH DAMAGE. */ +#include "linker.h" +#include "linker_format.h" + #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -36,45 +39,35 @@ #include <sys/socket.h> #include <sys/un.h> -extern int tgkill(int tgid, int tid, int sig); +#include <private/logd.h> -void notify_gdb_of_libraries(); +extern "C" int tgkill(int tgid, int tid, int sig); #define DEBUGGER_SOCKET_NAME "android:debuggerd" -typedef enum { +enum debugger_action_t { // dump a crash DEBUGGER_ACTION_CRASH, // dump a tombstone file DEBUGGER_ACTION_DUMP_TOMBSTONE, // dump a backtrace only back to the socket DEBUGGER_ACTION_DUMP_BACKTRACE, -} debugger_action_t; +}; /* message sent over the socket */ -typedef struct { +struct debugger_msg_t { debugger_action_t action; pid_t tid; -} debugger_msg_t; - -#define RETRY_ON_EINTR(ret,cond) \ - do { \ - ret = (cond); \ - } while (ret < 0 && errno == EINTR) +}; // see man(2) prctl, specifically the section about PR_GET_NAME #define MAX_TASK_NAME_LEN (16) -static int socket_abstract_client(const char *name, int type) -{ - struct sockaddr_un addr; - size_t namelen; - socklen_t alen; - int s, err; - - namelen = strlen(name); +static int socket_abstract_client(const char* name, int type) { + sockaddr_un addr; // Test with length +1 for the *initial* '\0'. + size_t namelen = strlen(name); if ((namelen + 1) > sizeof(addr.sun_path)) { errno = EINVAL; return -1; @@ -86,18 +79,20 @@ static int socket_abstract_client(const char *name, int type) * Note: The path in this case is *not* supposed to be * '\0'-terminated. ("man 7 unix" for the gory details.) */ - memset (&addr, 0, sizeof addr); + memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = 0; memcpy(addr.sun_path + 1, name, namelen); - alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; + socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1; - s = socket(AF_LOCAL, type, 0); - if(s < 0) return -1; + int s = socket(AF_LOCAL, type, 0); + if (s == -1) { + return -1; + } - RETRY_ON_EINTR(err,connect(s, (struct sockaddr *) &addr, alen)); - if (err < 0) { + int err = TEMP_FAILURE_RETRY(connect(s, (sockaddr*) &addr, alen)); + if (err == -1) { close(s); s = -1; } @@ -105,9 +100,6 @@ static int socket_abstract_client(const char *name, int type) return s; } -#include "linker_format.h" -#include <../libc/private/logd.h> - /* * Writes a summary of the signal to the log file. We do this so that, if * for some reason we're not able to contact debuggerd, there is still some @@ -116,15 +108,9 @@ static int socket_abstract_client(const char *name, int type) * We could be here as a result of native heap corruption, or while a * mutex is being held, so we don't want to use any libc functions that * could allocate memory or hold a lock. - * - * "info" will be NULL if the siginfo_t information was not available. */ -static void logSignalSummary(int signum, const siginfo_t* info) -{ - char buffer[128]; - char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination - - char* signame; +static void logSignalSummary(int signum, const siginfo_t* info) { + const char* signame; switch (signum) { case SIGILL: signame = "SIGILL"; break; case SIGABRT: signame = "SIGABRT"; break; @@ -138,6 +124,7 @@ static void logSignalSummary(int signum, const siginfo_t* info) default: signame = "???"; break; } + char threadname[MAX_TASK_NAME_LEN + 1]; // one more for termination if (prctl(PR_GET_NAME, (unsigned long)threadname, 0, 0, 0) != 0) { strcpy(threadname, "<name unknown>"); } else { @@ -145,6 +132,9 @@ static void logSignalSummary(int signum, const siginfo_t* info) // implies that 16 byte names are not. threadname[MAX_TASK_NAME_LEN] = 0; } + + char buffer[128]; + // "info" will be NULL if the siginfo_t information was not available. if (info != NULL) { format_buffer(buffer, sizeof(buffer), "Fatal signal %d (%s) at 0x%08x (code=%d), thread %d (%s)", @@ -161,8 +151,7 @@ static void logSignalSummary(int signum, const siginfo_t* info) /* * Returns true if the handler for signal "signum" has SA_SIGINFO set. */ -static bool haveSiginfo(int signum) -{ +static bool haveSiginfo(int signum) { struct sigaction oldact, newact; memset(&newact, 0, sizeof(newact)); @@ -188,11 +177,8 @@ static bool haveSiginfo(int signum) * Catches fatal signals so we can ask debuggerd to ptrace us before * we crash. */ -void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__((unused))) -{ +void debugger_signal_handler(int n, siginfo_t* info, void*) { char msgbuf[128]; - unsigned tid; - int s; /* * It's possible somebody cleared the SA_SIGINFO flag, which would mean @@ -204,8 +190,8 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__( logSignalSummary(n, info); - tid = gettid(); - s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); + pid_t tid = gettid(); + int s = socket_abstract_client(DEBUGGER_SOCKET_NAME, SOCK_STREAM); if (s >= 0) { /* debugger knows our pid from the credentials on the @@ -217,14 +203,14 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__( debugger_msg_t msg; msg.action = DEBUGGER_ACTION_CRASH; msg.tid = tid; - RETRY_ON_EINTR(ret, write(s, &msg, sizeof(msg))); + ret = TEMP_FAILURE_RETRY(write(s, &msg, sizeof(msg))); if (ret == sizeof(msg)) { /* if the write failed, there is no point to read on * the file descriptor. */ - RETRY_ON_EINTR(ret, read(s, &tid, 1)); - int savedErrno = errno; + ret = TEMP_FAILURE_RETRY(read(s, &tid, 1)); + int saved_errno = errno; notify_gdb_of_libraries(); - errno = savedErrno; + errno = saved_errno; } if (ret < 0) { @@ -266,8 +252,7 @@ void debugger_signal_handler(int n, siginfo_t* info, void* unused __attribute__( } } -void debugger_init() -{ +void debugger_init() { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = debugger_signal_handler; diff --git a/linker/linker.cpp b/linker/linker.cpp index 4ffa1e7..4c52f3d 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -93,7 +93,6 @@ static soinfo *sonext = &libdl_info; static soinfo *somain; /* main process, always the one after libdl_info */ #endif - static char ldpaths_buf[LDPATH_BUFSIZE]; static const char *ldpaths[LDPATH_MAX + 1]; @@ -108,9 +107,6 @@ int debug_verbosity; static int pid; -/* This boolean is set if the program being loaded is setuid */ -static bool program_is_setuid; - enum RelocationKind { kRelocAbsolute = 0, kRelocRelative, @@ -257,8 +253,7 @@ static void notify_gdb_of_unload(soinfo* info) { rtld_db_dlactivity(); } -extern "C" void notify_gdb_of_libraries() -{ +void notify_gdb_of_libraries() { _r_debug.r_state = RT_ADD; rtld_db_dlactivity(); _r_debug.r_state = RT_CONSISTENT; @@ -1689,7 +1684,7 @@ static int soinfo_link_image(soinfo *si) ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-02:23.stdio.asc */ - if (program_is_setuid) { + if (get_AT_SECURE()) { nullify_closed_stdio(); } notify_gdb_of_load(si); @@ -1750,11 +1745,6 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke int argc = (int) *elfdata; char **argv = (char**) (elfdata + 1); unsigned *vecs = (unsigned*) (argv + argc + 1); - unsigned *v; - soinfo *si; - int i; - const char *ldpath_env = NULL; - const char *ldpreload_env = NULL; /* NOTE: we store the elfdata pointer on a special location * of the temporary TLS area in order to pass it to @@ -1773,53 +1763,36 @@ static unsigned __linker_init_post_relocation(unsigned **elfdata, unsigned linke gettimeofday(&t0, 0); #endif - /* Initialize environment functions, and get to the ELF aux vectors table */ + // Initialize environment functions, and get to the ELF aux vectors table. vecs = linker_env_init(vecs); - /* Check auxv for AT_SECURE first to see if program is setuid, setgid, - has file caps, or caused a SELinux/AppArmor domain transition. */ - for (v = vecs; v[0]; v += 2) { - if (v[0] == AT_SECURE) { - /* kernel told us whether to enable secure mode */ - program_is_setuid = v[1]; - goto sanitize; - } - } - - /* Kernel did not provide AT_SECURE - fall back on legacy test. */ - program_is_setuid = (getuid() != geteuid()) || (getgid() != getegid()); - -sanitize: - /* Sanitize environment if we're loading a setuid program */ - if (program_is_setuid) { - linker_env_secure(); - } - debugger_init(); - /* Get a few environment variables */ - { + // Get a few environment variables. #if LINKER_DEBUG - const char* env; - env = linker_env_get("DEBUG"); /* XXX: TODO: Change to LD_DEBUG */ - if (env) + { + const char* env = linker_env_get("LD_DEBUG"); + if (env != NULL) { debug_verbosity = atoi(env); + } + } #endif - /* Normally, these are cleaned by linker_env_secure, but the test - * against program_is_setuid doesn't cost us anything */ - if (!program_is_setuid) { - ldpath_env = linker_env_get("LD_LIBRARY_PATH"); - ldpreload_env = linker_env_get("LD_PRELOAD"); - } + // Normally, these are cleaned by linker_env_init, but the test + // doesn't cost us anything. + const char* ldpath_env = NULL; + const char* ldpreload_env = NULL; + if (!get_AT_SECURE()) { + ldpath_env = linker_env_get("LD_LIBRARY_PATH"); + ldpreload_env = linker_env_get("LD_PRELOAD"); } INFO("[ android linker & debugger ]\n"); DEBUG("%5d elfdata @ 0x%08x\n", pid, (unsigned)elfdata); - si = soinfo_alloc(argv[0]); - if(si == 0) { - exit(-1); + soinfo* si = soinfo_alloc(argv[0]); + if (si == NULL) { + exit(EXIT_FAILURE); } /* bootstrap the link map, the main exe always needs to be first */ @@ -1858,7 +1831,7 @@ sanitize: insert_soinfo_into_debug_map(&linker_soinfo); /* extract information passed from the kernel */ - while(vecs[0] != 0){ + while (vecs[0] != 0){ switch(vecs[0]){ case AT_PHDR: si->phdr = (Elf32_Phdr*) vecs[1]; @@ -1899,12 +1872,12 @@ sanitize: char errmsg[] = "CANNOT LINK EXECUTABLE\n"; write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf)); write(2, errmsg, sizeof(errmsg)); - exit(-1); + exit(EXIT_FAILURE); } soinfo_call_preinit_constructors(si); - for(i = 0; preloads[i] != NULL; i++) { + for (size_t i = 0; preloads[i] != NULL; ++i) { soinfo_call_constructors(preloads[i]); } @@ -2049,7 +2022,7 @@ extern "C" unsigned __linker_init(unsigned **elfdata) { // // This situation should never occur unless the linker itself // is corrupt. - exit(-1); + exit(EXIT_FAILURE); } // We have successfully fixed our own relocations. It's safe to run diff --git a/linker/linker.h b/linker/linker.h index 8ed433c..54563bb 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -34,9 +34,6 @@ #include <elf.h> #include <sys/exec_elf.h> -#ifdef __cplusplus -extern "C" { -#endif #include <link.h> #undef PAGE_MASK @@ -89,8 +86,6 @@ struct r_debug uintptr_t r_ldbase; }; -typedef struct soinfo soinfo; - #define FLAG_LINKED 0x00000001 #define FLAG_ERROR 0x00000002 #define FLAG_EXE 0x00000004 // The main executable @@ -98,8 +93,7 @@ typedef struct soinfo soinfo; #define SOINFO_NAME_LEN 128 -struct soinfo -{ +struct soinfo { char name[SOINFO_NAME_LEN]; const Elf32_Phdr *phdr; int phnum; @@ -232,8 +226,6 @@ Elf32_Sym *soinfo_find_symbol(soinfo* si, const void *addr); Elf32_Sym *soinfo_lookup(soinfo *si, const char *name); void soinfo_call_constructors(soinfo *si); -#ifdef __cplusplus -}; -#endif +extern "C" void notify_gdb_of_libraries(); #endif diff --git a/linker/linker_debug.h b/linker/linker_debug.h index 48a7abf..b9dfe34 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -62,10 +62,6 @@ #if LINKER_DEBUG #include "linker_format.h" -#ifdef __cplusplus -extern "C" { -#endif - extern int debug_verbosity; #if LINKER_DEBUG_TO_LOG extern int format_log(int, const char *, const char *, ...); @@ -81,10 +77,6 @@ extern int format_fd(int, const char *, ...); } while (0) #endif /* !LINKER_DEBUG_TO_LOG */ -#ifdef __cplusplus -}; -#endif - #else /* !LINKER_DEBUG */ #define _PRINTVF(v,f,x...) do {} while(0) #endif /* LINKER_DEBUG */ diff --git a/linker/linker_environ.c b/linker/linker_environ.c deleted file mode 100644 index fadcb60..0000000 --- a/linker/linker_environ.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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 COPYRIGHT HOLDERS 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 - * COPYRIGHT OWNER 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 "linker_environ.h" -#include <stddef.h> - -static char** _envp; - -/* Returns 1 if 'str' points to a valid environment variable definition. - * For now, we check that: - * - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings) - * - It contains at least one equal sign that is not the first character - */ -static int -_is_valid_definition(const char* str) -{ - int pos = 0; - int first_equal_pos = -1; - - /* According to its sources, the kernel uses 32*PAGE_SIZE by default - * as the maximum size for an env. variable definition. - */ - const int MAX_ENV_LEN = 32*4096; - - if (str == NULL) - return 0; - - /* Parse the string, looking for the first '=' there, and its size */ - do { - if (str[pos] == '\0') - break; - if (str[pos] == '=' && first_equal_pos < 0) - first_equal_pos = pos; - pos++; - } while (pos < MAX_ENV_LEN); - - if (pos >= MAX_ENV_LEN) /* Too large */ - return 0; - - if (first_equal_pos < 1) /* No equal sign, or it is the first character */ - return 0; - - return 1; -} - -unsigned* -linker_env_init(unsigned* vecs) -{ - /* Store environment pointer - can't be NULL */ - _envp = (char**) vecs; - - /* Skip over all definitions */ - while (vecs[0] != 0) - vecs++; - /* The end of the environment block is marked by two NULL pointers */ - vecs++; - - /* As a sanity check, we're going to remove all invalid variable - * definitions from the environment array. - */ - { - char** readp = _envp; - char** writep = _envp; - for ( ; readp[0] != NULL; readp++ ) { - if (!_is_valid_definition(readp[0])) - continue; - writep[0] = readp[0]; - writep++; - } - writep[0] = NULL; - } - - /* Return the address of the aux vectors table */ - return vecs; -} - -/* Check if the environment variable definition at 'envstr' - * starts with '<name>=', and if so return the address of the - * first character after the equal sign. Otherwise return NULL. - */ -static char* -env_match(char* envstr, const char* name) -{ - size_t cnt = 0; - - while (envstr[cnt] == name[cnt] && name[cnt] != '\0') - cnt++; - - if (name[cnt] == '\0' && envstr[cnt] == '=') - return envstr + cnt + 1; - - return NULL; -} - -#define MAX_ENV_LEN (16*4096) - -const char* -linker_env_get(const char* name) -{ - char** readp = _envp; - - if (name == NULL || name[0] == '\0') - return NULL; - - for ( ; readp[0] != NULL; readp++ ) { - char* val = env_match(readp[0], name); - if (val != NULL) { - /* Return NULL for empty strings, or if it is too large */ - if (val[0] == '\0') - val = NULL; - return val; - } - } - return NULL; -} - - -void -linker_env_unset(const char* name) -{ - char** readp = _envp; - char** writep = readp; - - if (name == NULL || name[0] == '\0') - return; - - for ( ; readp[0] != NULL; readp++ ) { - if (env_match(readp[0], name)) - continue; - writep[0] = readp[0]; - writep++; - } - /* end list with a NULL */ - writep[0] = NULL; -} - - - -/* Remove unsafe environment variables. This should be used when - * running setuid programs. */ -void -linker_env_secure(void) -{ - /* The same list than GLibc at this point */ - static const char* const unsec_vars[] = { - "GCONV_PATH", - "GETCONF_DIR", - "HOSTALIASES", - "LD_AUDIT", - "LD_DEBUG", - "LD_DEBUG_OUTPUT", - "LD_DYNAMIC_WEAK", - "LD_LIBRARY_PATH", - "LD_ORIGIN_PATH", - "LD_PRELOAD", - "LD_PROFILE", - "LD_SHOW_AUXV", - "LD_USE_LOAD_BIAS", - "LOCALDOMAIN", - "LOCPATH", - "MALLOC_TRACE", - "MALLOC_CHECK_", - "NIS_PATH", - "NLSPATH", - "RESOLV_HOST_CONF", - "RES_OPTIONS", - "TMPDIR", - "TZDIR", - "LD_AOUT_LIBRARY_PATH", - "LD_AOUT_PRELOAD", - NULL - }; - - int count; - for (count = 0; unsec_vars[count] != NULL; count++) { - linker_env_unset(unsec_vars[count]); - } -} diff --git a/linker/linker_environ.cpp b/linker/linker_environ.cpp new file mode 100644 index 0000000..357be6d --- /dev/null +++ b/linker/linker_environ.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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 "linker_environ.h" + +#include <linux/auxvec.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> + +static char** _envp; +static bool _AT_SECURE_value = true; + +bool get_AT_SECURE() { + return _AT_SECURE_value; +} + +/* Returns 1 if 'str' points to a valid environment variable definition. + * For now, we check that: + * - It is smaller than MAX_ENV_LEN (to detect non-zero terminated strings) + * - It contains at least one equal sign that is not the first character + */ +static int _is_valid_definition(const char* str) { + int pos = 0; + int first_equal_pos = -1; + + // According to its sources, the kernel uses 32*PAGE_SIZE by default + // as the maximum size for an env. variable definition. + const int MAX_ENV_LEN = 32*4096; + + if (str == NULL) { + return 0; + } + + // Parse the string, looking for the first '=' there, and its size. + while (pos < MAX_ENV_LEN) { + if (str[pos] == '\0') { + break; + } + if (str[pos] == '=' && first_equal_pos < 0) { + first_equal_pos = pos; + } + pos++; + } + + if (pos >= MAX_ENV_LEN) { + return 0; // Too large. + } + + if (first_equal_pos < 1) { + return 0; // No equals sign, or it's the first character. + } + + return 1; +} + +static void __init_AT_SECURE(unsigned* auxv) { + // Check auxv for AT_SECURE first to see if program is setuid, setgid, + // has file caps, or caused a SELinux/AppArmor domain transition. + for (unsigned* v = auxv; v[0]; v += 2) { + if (v[0] == AT_SECURE) { + // Kernel told us whether to enable secure mode. + _AT_SECURE_value = v[1]; + return; + } + } + + // We don't support ancient kernels. + const char* msg = "FATAL: kernel did not supply AT_SECURE\n"; + write(2, msg, strlen(msg)); + exit(EXIT_FAILURE); +} + +static void __remove_unsafe_environment_variables() { + // None of these should be allowed in setuid programs. + static const char* const UNSAFE_VARIABLE_NAMES[] = { + "GCONV_PATH", + "GETCONF_DIR", + "HOSTALIASES", + "LD_AOUT_LIBRARY_PATH", + "LD_AOUT_PRELOAD", + "LD_AUDIT", + "LD_DEBUG", + "LD_DEBUG_OUTPUT", + "LD_DYNAMIC_WEAK", + "LD_LIBRARY_PATH", + "LD_ORIGIN_PATH", + "LD_PRELOAD", + "LD_PROFILE", + "LD_SHOW_AUXV", + "LD_USE_LOAD_BIAS", + "LOCALDOMAIN", + "LOCPATH", + "MALLOC_CHECK_", + "MALLOC_TRACE", + "NIS_PATH", + "NLSPATH", + "RESOLV_HOST_CONF", + "RES_OPTIONS", + "TMPDIR", + "TZDIR", + NULL + }; + for (size_t i = 0; UNSAFE_VARIABLE_NAMES[i] != NULL; ++i) { + linker_env_unset(UNSAFE_VARIABLE_NAMES[i]); + } +} + +static void __remove_invalid_environment_variables() { + char** src = _envp; + char** dst = _envp; + for (; src[0] != NULL; ++src) { + if (!_is_valid_definition(src[0])) { + continue; + } + dst[0] = src[0]; + ++dst; + } + dst[0] = NULL; +} + +unsigned* linker_env_init(unsigned* environment_and_aux_vectors) { + // Store environment pointer - can't be NULL. + _envp = reinterpret_cast<char**>(environment_and_aux_vectors); + + // Skip over all environment variable definitions. + // The end of the environment block is marked by two NULL pointers. + unsigned* aux_vectors = environment_and_aux_vectors; + while (aux_vectors[0] != 0) { + ++aux_vectors; + } + ++aux_vectors; + + __remove_invalid_environment_variables(); + __init_AT_SECURE(aux_vectors); + + // Sanitize environment if we're loading a setuid program. + if (get_AT_SECURE()) { + __remove_unsafe_environment_variables(); + } + + return aux_vectors; +} + +/* Check if the environment variable definition at 'envstr' + * starts with '<name>=', and if so return the address of the + * first character after the equal sign. Otherwise return NULL. + */ +static char* env_match(char* envstr, const char* name) { + size_t i = 0; + + while (envstr[i] == name[i] && name[i] != '\0') { + ++i; + } + + if (name[i] == '\0' && envstr[i] == '=') { + return envstr + i + 1; + } + + return NULL; +} + +const char* linker_env_get(const char* name) { + if (name == NULL || name[0] == '\0') { + return NULL; + } + + for (char** p = _envp; p[0] != NULL; ++p) { + char* val = env_match(p[0], name); + if (val != NULL) { + if (val[0] == '\0') { + return NULL; // Return NULL for empty strings. + } + return val; + } + } + return NULL; +} + +void linker_env_unset(const char* name) { + char** readp = _envp; + char** writep = readp; + + if (name == NULL || name[0] == '\0') { + return; + } + + for ( ; readp[0] != NULL; readp++ ) { + if (env_match(readp[0], name)) { + continue; + } + writep[0] = readp[0]; + writep++; + } + /* end list with a NULL */ + writep[0] = NULL; +} diff --git a/linker/linker_environ.h b/linker/linker_environ.h index d5f75a1..a0bd69f 100644 --- a/linker/linker_environ.h +++ b/linker/linker_environ.h @@ -25,38 +25,27 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #ifndef LINKER_ENVIRON_H #define LINKER_ENVIRON_H -#ifdef __cplusplus -extern "C" { -#endif - -/* Call this function before anything else. 'vecs' must be the pointer - * to the environment block in the ELF data block. The function returns - * the start of the aux vectors after the env block. - */ -extern unsigned* linker_env_init(unsigned* vecs); +// Call this function before anything else. 'environment_and_aux_vectors' +// must point to the environment block in the ELF data block. The function +// returns the start of the aux vectors after the environment block. +extern unsigned* linker_env_init(unsigned* environment_and_aux_vectors); -/* Unset a given environment variable. In case the variable is defined - * multiple times, unset all instances. This modifies the environment - * block, so any pointer returned by linker_env_get() after this call - * might become invalid */ -extern void linker_env_unset(const char* name); +// Unset a given environment variable. In case the variable is defined +// multiple times, unset all instances. This modifies the environment +// block, so any pointer returned by linker_env_get() after this call +// might become invalid. +extern void linker_env_unset(const char* name); - -/* Returns the value of environment variable 'name' if defined and not - * empty, or NULL otherwise. Note that the returned pointer may become - * invalid if linker_env_unset() or linker_env_secure() are called - * after this function. */ +// Returns the value of environment variable 'name' if defined and not +// empty, or NULL otherwise. Note that the returned pointer may become +// invalid if linker_env_unset() is called after this function. extern const char* linker_env_get(const char* name); -/* Remove insecure environment variables. This should be used when - * running setuid programs. */ -extern void linker_env_secure(void); - -#ifdef __cplusplus -}; -#endif +// Returns the value of this program's AT_SECURE variable. +extern bool get_AT_SECURE(); #endif /* LINKER_ENVIRON_H */ diff --git a/linker/linker_format.c b/linker/linker_format.cpp index f60e259..cc70a03 100644 --- a/linker/linker_format.c +++ b/linker/linker_format.cpp @@ -26,6 +26,7 @@ * SUCH DAMAGE. */ +#include <assert.h> #include <stdarg.h> #include <string.h> #include <errno.h> @@ -43,14 +44,12 @@ /*** Generic output sink ***/ -typedef struct { - void *opaque; - void (*send)(void *opaque, const char *data, int len); -} Out; +struct Out { + void *opaque; + void (*send)(void *opaque, const char *data, int len); +}; -static void -out_send(Out *o, const void *data, size_t len) -{ +static void out_send(Out *o, const char *data, size_t len) { o->send(o->opaque, data, (int)len); } @@ -72,27 +71,25 @@ out_send_repeat(Out *o, char ch, int count) } /* forward declaration */ -static void -out_vformat(Out *o, const char *format, va_list args); +static void out_vformat(Out* o, const char* format, va_list args); /*** Bounded buffer output ***/ -typedef struct { - Out out[1]; - char *buffer; - char *pos; - char *end; - int total; -} BufOut; +struct BufOut { + Out out[1]; + char *buffer; + char *pos; + char *end; + int total; +}; -static void -buf_out_send(void *opaque, const char *data, int len) -{ - BufOut *bo = opaque; +static void buf_out_send(void *opaque, const char *data, int len) { + BufOut *bo = reinterpret_cast<BufOut*>(opaque); - if (len < 0) + if (len < 0) { len = strlen(data); + } bo->total += len; @@ -194,11 +191,11 @@ snprintf(char* buff, size_t bufsize, const char* format, ...) /*** File descriptor output ***/ -typedef struct { - Out out[1]; - int fd; - int total; -} FdOut; +struct FdOut { + Out out[1]; + int fd; + int total; +}; static void fd_out_send(void *opaque, const char *data, int len) @@ -593,6 +590,10 @@ out_vformat(Out *o, const char *format, va_list args) slen = strlen(str); + if (sign != '\0' || prec != -1) { + __assert(__FILE__, __LINE__, "sign/precision unsupported"); + } + if (slen < width && !padLeft) { char padChar = padZero ? '0' : ' '; out_send_repeat(o, padChar, width - slen); diff --git a/linker/linker_format.h b/linker/linker_format.h index 3766b62..b8873c0 100644 --- a/linker/linker_format.h +++ b/linker/linker_format.h @@ -25,25 +25,17 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #ifndef _LINKER_FORMAT_H #define _LINKER_FORMAT_H #include <stdarg.h> #include <stddef.h> -#ifdef __cplusplus -extern "C" { -#endif - -/* Formatting routines for the dynamic linker's debug traces */ -/* We want to avoid dragging the whole C library fprintf() */ -/* implementation into the dynamic linker since this creates */ -/* issues (it uses malloc()/free()) and increases code size */ - +// Formatting routines for the dynamic linker's debug traces +// We want to avoid dragging the whole C library fprintf() +// implementation into the dynamic linker since this creates +// issues (it uses malloc()/free()) and increases code size. int format_buffer(char *buffer, size_t bufsize, const char *format, ...); -#ifdef __cplusplus -}; -#endif - #endif /* _LINKER_FORMAT_H */ diff --git a/linker/linker_phdr.c b/linker/linker_phdr.cpp index 250ca20..1990366 100644 --- a/linker/linker_phdr.c +++ b/linker/linker_phdr.cpp @@ -301,7 +301,6 @@ phdr_table_load_segments(const Elf32_Phdr* phdr_table, Elf32_Addr file_end = file_start + phdr->p_filesz; Elf32_Addr file_page_start = PAGE_START(file_start); - Elf32_Addr file_page_end = PAGE_END(file_end); seg_addr = mmap((void*)seg_page_start, file_end - file_page_start, diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index a759262..2d4d735 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -37,12 +37,6 @@ #include "linker.h" -#ifdef __cplusplus -extern "C" { -#endif - -/* See linker_phdr.c for all usage documentation */ - int phdr_table_load(int fd, Elf32_Addr phdr_offset, @@ -107,8 +101,4 @@ phdr_table_get_dynamic_section(const Elf32_Phdr* phdr_table, Elf32_Addr** dynamic, size_t* dynamic_count); -#ifdef __cplusplus -}; -#endif - #endif /* LINKER_PHDR_H */ diff --git a/linker/rt.c b/linker/rt.cpp index afbd651..710892a 100644 --- a/linker/rt.c +++ b/linker/rt.cpp @@ -28,9 +28,8 @@ /* * This function is an empty stub where GDB locates a breakpoint to get notified - * about linker activity. It canʼt be inlined away, canʼt be hidden. + * about linker activity. It canʼt be inlined away, can't be hidden. */ -void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity(void) -{ +extern "C" void __attribute__((noinline)) __attribute__((visibility("default"))) rtld_db_dlactivity() { } |