More dynamic linker cleanup.

I still want to break linker_format out into its own library so we can reuse
it for malloc debugging and so forth. (There are many similar pieces of code
in bionic, but the linker's one seems to be the most complete/functional.)

Change-Id: If3721853d28937c8e821ca1d23cf200e228a409a
This commit is contained in:
Elliott Hughes 2012-10-29 17:37:13 -07:00
parent 06b596104a
commit 18a206c81d
13 changed files with 336 additions and 405 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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 */

View File

@ -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]);
}
}

222
linker/linker_environ.cpp Normal file
View File

@ -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;
}

View File

@ -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. '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);
/* 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);
// 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 */

View File

@ -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);

View File

@ -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 */

View File

@ -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,

View File

@ -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 */

View File

@ -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() {
}