summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mooney <pmooney@pfmooney.com>2016-06-24 23:03:34 +0000
committerPatrick Mooney <pmooney@pfmooney.com>2016-06-27 18:13:23 +0000
commit39bc219977fe06d26b768d7ddb2b73603a734446 (patch)
tree33875a15ff858d90d97b3fe3e9c03f20fbb3f81e
parent3071a4eca9e68f51208292bb7cab2fb922be4e48 (diff)
downloadillumos-joyent-39bc219977fe06d26b768d7ddb2b73603a734446.tar.gz
OS-5477 lxbrand env manipulation causes ld-linux woes
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
-rw-r--r--usr/src/common/brand/lx/lx_auxv.c53
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/debug.c22
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c138
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/signal.c14
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h4
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h1
6 files changed, 156 insertions, 76 deletions
diff --git a/usr/src/common/brand/lx/lx_auxv.c b/usr/src/common/brand/lx/lx_auxv.c
index 8994ea31a5..2ed5fd0517 100644
--- a/usr/src/common/brand/lx/lx_auxv.c
+++ b/usr/src/common/brand/lx/lx_auxv.c
@@ -16,57 +16,68 @@
#include <sys/auxv.h>
#include <sys/lx_brand.h>
+/*
+ * Linux does not make the distinction between 'int' and 'long' when it comes
+ * to the format of the aux vector. In order to properly clear the struct
+ * padding present in the native auxv_t in 64-bit, we employ the Linux format.
+ */
+struct lx_auxv {
+ long la_type;
+ long la_val;
+};
int
lx_auxv_stol(const auxv_t *ap, auxv_t *oap, const lx_elf_data_t *edp)
{
+ struct lx_auxv *loap = (struct lx_auxv *)oap;
+
switch (ap->a_type) {
case AT_BASE:
- oap->a_un.a_val = edp->ed_base;
+ loap->la_val = edp->ed_base;
break;
case AT_ENTRY:
- oap->a_un.a_val = edp->ed_entry;
+ loap->la_val = edp->ed_entry;
break;
case AT_PHDR:
- oap->a_un.a_val = edp->ed_phdr;
+ loap->la_val = edp->ed_phdr;
break;
case AT_PHENT:
- oap->a_un.a_val = edp->ed_phent;
+ loap->la_val = edp->ed_phent;
break;
case AT_PHNUM:
- oap->a_un.a_val = edp->ed_phnum;
+ loap->la_val = edp->ed_phnum;
break;
case AT_SUN_BRAND_LX_SYSINFO_EHDR:
- oap->a_type = AT_SYSINFO_EHDR;
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_type = AT_SYSINFO_EHDR;
+ loap->la_val = ap->a_un.a_val;
return (0);
case AT_SUN_BRAND_LX_CLKTCK:
- oap->a_type = AT_CLKTCK;
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_type = AT_CLKTCK;
+ loap->la_val = ap->a_un.a_val;
return (0);
case AT_SUN_AUXFLAGS:
if ((ap->a_un.a_val & AF_SUN_SETUGID) != 0) {
- oap->a_type = AT_SECURE;
- oap->a_un.a_val = 1;
+ loap->la_type = AT_SECURE;
+ loap->la_val = 1;
return (0);
} else {
return (1);
}
case AT_SUN_GID:
- oap->a_type = AT_LX_EGID;
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_type = AT_LX_EGID;
+ loap->la_val = ap->a_un.a_val;
return (0);
case AT_SUN_RGID:
- oap->a_type = AT_LX_GID;
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_type = AT_LX_GID;
+ loap->la_val = ap->a_un.a_val;
return (0);
case AT_SUN_UID:
- oap->a_type = AT_LX_EUID;
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_type = AT_LX_EUID;
+ loap->la_val = ap->a_un.a_val;
return (0);
case AT_SUN_RUID:
- oap->a_type = AT_LX_UID;
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_type = AT_LX_UID;
+ loap->la_val = ap->a_un.a_val;
return (0);
case AT_EXECFD:
case AT_PAGESZ:
@@ -74,12 +85,12 @@ lx_auxv_stol(const auxv_t *ap, auxv_t *oap, const lx_elf_data_t *edp)
case AT_RANDOM:
case AT_NULL:
/* No translate needed */
- oap->a_un.a_val = ap->a_un.a_val;
+ loap->la_val = ap->a_un.a_val;
break;
default:
/* All other unrecognized entries are ignored */
return (1);
}
- oap->a_type = ap->a_type;
+ loap->la_type = ap->a_type;
return (0);
}
diff --git a/usr/src/lib/brand/lx/lx_brand/common/debug.c b/usr/src/lib/brand/lx/lx_brand/common/debug.c
index 7e58a0f941..a8f994c43d 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/debug.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/debug.c
@@ -21,7 +21,7 @@
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <assert.h>
@@ -43,21 +43,13 @@
#include <sys/lx_misc.h>
/* internal debugging state */
-static char *lx_debug_path = NULL; /* debug output file path */
-static char lx_debug_path_buf[MAXPATHLEN];
+static const char *lx_debug_path = NULL; /* debug output file path */
+static char lx_debug_path_buf[MAXPATHLEN];
int lx_dtrace_lazyload = 1; /* patchable; see below */
void
-lx_debug_enable(void)
-{
- /* send all debugging output to /dev/tty */
- lx_debug_path = "/dev/tty";
- lx_debug("lx_debug: debugging output enabled: %s", lx_debug_path);
-}
-
-void
-lx_debug_init(void)
+lx_debug_init(boolean_t do_dtrace, boolean_t dbg_enable, const char *dbg_file)
{
/*
* Our DTrace USDT provider is loaded in our .init section, which is
@@ -72,12 +64,12 @@ lx_debug_init(void)
* provided in situations where setting an environment variable is
* tedious or otherwise impossible.
*/
- if (getenv("LX_DTRACE") != NULL || !lx_dtrace_lazyload) {
+ if (do_dtrace || !lx_dtrace_lazyload) {
extern void _init(void);
_init();
}
- if (getenv("LX_DEBUG") == NULL)
+ if (!dbg_enable)
return;
/*
@@ -92,7 +84,7 @@ lx_debug_init(void)
lx_debug_enabled = 1;
/* check if there's a debug log file specified */
- lx_debug_path = getenv("LX_DEBUG_FILE");
+ lx_debug_path = dbg_file;
if (lx_debug_path == NULL) {
/* send all debugging output to /dev/tty */
lx_debug_path = "/dev/tty";
diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
index 75fba80b07..39fd64e4e0 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
@@ -89,6 +89,7 @@
char lx_release[LX_KERN_RELEASE_MAX];
char lx_cmd_name[MAXNAMLEN];
+boolean_t lx_no_abort_handler = B_FALSE;
/*
* Map a linux locale ending string to the solaris equivalent.
@@ -558,17 +559,71 @@ lx_start(uintptr_t sp, uintptr_t entry)
lx_jump_to_linux(&jump_uc);
}
+enum lx_env_setting {
+ LXES_INSTALL = 0,
+ LXES_VERBOSE,
+ LXES_DTRACE,
+ LXES_DEBUG,
+ LXES_DEBUG_FILE,
+ LXES_NO_ABORT_HANDLER,
+ LXES_RELEASE,
+ LXES_VERSION,
+ LXES_STRICT,
+ LXES_LIMIT
+};
+
+static void
+lx_parse_env(char *envp[], char *settings[])
+{
+ int i, j;
+ char *env;
+
+ typedef struct lx_env_entry {
+ char *lee_name;
+ int lee_len;
+ int lee_index;
+ } lx_env_entry_t;
+#define LX_ENV_ENTRY(name, idx) { name, (sizeof (name)) - 1, idx }
+ static const lx_env_entry_t lx_env_entries[] = {
+ LX_ENV_ENTRY("LX_INSTALL", LXES_INSTALL),
+ LX_ENV_ENTRY("LX_VERBOSE", LXES_VERBOSE),
+ LX_ENV_ENTRY("LX_DTRACE", LXES_DTRACE),
+ LX_ENV_ENTRY("LX_DEBUG", LXES_DEBUG),
+ LX_ENV_ENTRY("LX_DEBUG_FILE", LXES_DEBUG_FILE),
+ LX_ENV_ENTRY("LX_NO_ABORT_HANDLER", LXES_NO_ABORT_HANDLER),
+ LX_ENV_ENTRY("LX_RELEASE", LXES_RELEASE),
+ LX_ENV_ENTRY("LX_VERSION", LXES_VERSION),
+ LX_ENV_ENTRY("LX_STRICT", LXES_STRICT)
+ };
+#define LX_ENV_ENTRY_COUNT \
+ (sizeof (lx_env_entries) / sizeof (lx_env_entries[0]))
+
+ for (i = 0; (env = envp[i]) != NULL; i++) {
+ if (env[0] != 'L' || env[1] != 'X' || env[2] != '_')
+ continue;
+ for (j = 0; j < LX_ENV_ENTRY_COUNT; j++) {
+ const lx_env_entry_t *lee = &lx_env_entries[j];
+
+ if (strncmp(env, lee->lee_name, lee->lee_len) != 0 ||
+ env[lee->lee_len] != '=')
+ continue;
+ settings[lee->lee_index] = &env[lee->lee_len + 1];
+ break;
+ }
+ }
+}
+
/*ARGSUSED*/
int
lx_init(int argc, char *argv[], char *envp[])
{
- char *rele, *vers;
auxv_t *ap, *oap;
long *p;
int err;
lx_elf_data_t edp;
lx_brand_registration_t reg;
lx_tsd_t *lxtsd;
+ char *lx_settings[LXES_LIMIT];
bzero(&reg, sizeof (reg));
stack_size = 2 * sysconf(_SC_PAGESIZE);
@@ -583,21 +638,44 @@ lx_init(int argc, char *argv[], char *envp[])
lx_close_fh(stdout);
lx_close_fh(stderr);
- lx_debug_init();
+ /*
+ * Parse LX-related settings out of the environment array.
+ * This is done manually instead of utilizing libc's getenv() to avoid
+ * triggering any env-cleaning routines which are present.
+ */
+ bzero(lx_settings, sizeof (lx_settings));
+ lx_parse_env(envp, lx_settings);
+
+ /*
+ * Setting LX_NO_ABORT_HANDLER in the environment will prevent the
+ * emulated Linux program from modifying the signal handling
+ * disposition for SIGSEGV or SIGABRT. It is useful for debugging
+ * programs which fall over themselves to prevent useful core files
+ * being generated.
+ */
+ lx_no_abort_handler = (lx_settings[LXES_NO_ABORT_HANDLER] != NULL);
+
+ lx_debug_init(lx_settings[LXES_DTRACE] != NULL,
+ lx_settings[LXES_DEBUG] != NULL,
+ lx_settings[LXES_DEBUG_FILE]);
- rele = getenv("LX_RELEASE");
- vers = getenv("LX_VERSION");
- if (rele == NULL) {
+ if (lx_settings[LXES_RELEASE] == NULL) {
if (zone_getattr(getzoneid(), LX_ATTR_KERN_RELEASE,
lx_release, sizeof (lx_release)) <= 0)
(void) strlcpy(lx_release, "2.4.21",
LX_KERN_RELEASE_MAX);
} else {
- (void) strlcpy(lx_release, rele, LX_KERN_RELEASE_MAX);
+ (void) strlcpy(lx_release, lx_settings[LXES_RELEASE],
+ LX_KERN_RELEASE_MAX);
}
- if (syscall(SYS_brand, B_OVERRIDE_KERN_VER, rele, vers) != 0) {
- lx_debug("failed to override kernel release/version");
+ if (lx_settings[LXES_RELEASE] != NULL ||
+ lx_settings[LXES_VERSION] != NULL) {
+ if (syscall(SYS_brand, B_OVERRIDE_KERN_VER,
+ lx_settings[LXES_RELEASE],
+ lx_settings[LXES_VERSION]) != 0) {
+ lx_debug("failed to override kernel release/version");
+ }
}
lx_debug("lx_release: %s\n", lx_release);
@@ -606,7 +684,7 @@ lx_init(int argc, char *argv[], char *envp[])
* Should we kill an application that attempts an unimplemented
* system call?
*/
- if (getenv("LX_STRICT") != NULL) {
+ if (lx_settings[LXES_STRICT] != NULL) {
reg.lxbr_flags |= LX_PROC_STRICT_MODE;
lx_debug("STRICT mode enabled.\n");
}
@@ -614,20 +692,12 @@ lx_init(int argc, char *argv[], char *envp[])
/*
* Are we in install mode?
*/
- if (getenv("LX_INSTALL") != NULL) {
+ if (lx_settings[LXES_INSTALL] != NULL) {
reg.lxbr_flags |= LX_PROC_INSTALL_MODE;
lx_install = 1;
lx_debug("INSTALL mode enabled.\n");
}
- /*
- * Should we attempt to send messages to the screen?
- */
- if (getenv("LX_VERBOSE") != NULL) {
- lx_verbose = 1;
- lx_debug("VERBOSE mode enabled.\n");
- }
-
(void) strlcpy(lx_cmd_name, basename(argv[0]), sizeof (lx_cmd_name));
lx_debug("executing linux process: %s", argv[0]);
lx_debug("branding myself and setting handler to 0x%p",
@@ -665,12 +735,31 @@ lx_init(int argc, char *argv[], char *envp[])
p = (long *)envp;
while (*p != NULL)
p++;
+
/*
- * p is now pointing at the 0 word after the environ pointers. After
- * that is the aux vectors.
+ * Now 'p' points at the NULL word immediately following the environ
+ * pointers. The list of auxv entries _should_ immediately follow.
+ * If anything (such as the native linker or libc) has removed entries
+ * from the environment array, extra NULLs will be present.
+ *
+ * The brand library takes care to avoid such behavior (via the
+ * lx_parse_env routine above) but a belt-and-suspenders approach is
+ * taken for safety.
+ *
+ * The address following the NULL spacer is recorded as the target for
+ * auxv translation and any addition NULLs following it are skipped
+ * until the first auxv entry is located.
*/
p++;
- for (ap = (auxv_t *)p, oap = ap; ap->a_type != AT_NULL; ap++) {
+ oap = (auxv_t *)p;
+ while (*p == NULL)
+ p++;
+ ap = (auxv_t *)p;
+
+ /*
+ * Translate auxv entries to Linux equivalents.
+ */
+ for (; ap->a_type != AT_NULL; ap++) {
if (lx_auxv_stol(ap, oap, &edp) == 0) {
/*
* Copy only auxv entries which Linux programs will
@@ -679,11 +768,10 @@ lx_init(int argc, char *argv[], char *envp[])
oap++;
}
}
+
/* NULL out skipped entries */
- while (oap < ap) {
- oap->a_type = AT_NULL;
- oap->a_un.a_val = 0;
- oap++;
+ if (oap < ap) {
+ bzero(oap, (uintptr_t)ap - (uintptr_t)oap);
}
/* Setup signal handler information. */
diff --git a/usr/src/lib/brand/lx/lx_brand/common/signal.c b/usr/src/lib/brand/lx/lx_brand/common/signal.c
index 7d07dfe66d..665c8c97bd 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/signal.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/signal.c
@@ -304,14 +304,6 @@ struct lx_oldsigstack {
*/
static lx_sighandlers_t lx_sighandlers;
-/*
- * Setting LX_NO_ABORT_HANDLER in the environment will prevent the emulated
- * Linux program from modifying the signal handling disposition for SIGSEGV or
- * SIGABRT. Useful for debugging programs which fall over themselves to
- * prevent useful core files being generated.
- */
-static int lx_no_abort_handler = 0;
-
static void lx_sigdeliver(int, siginfo_t *, ucontext_t *, size_t, void (*)(),
void (*)(), struct lx_sigaction *);
@@ -1913,7 +1905,7 @@ lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp,
return (-errno);
if ((sig = ltos_signo[lx_sig]) != -1) {
- if (lx_no_abort_handler != 0) {
+ if (lx_no_abort_handler) {
/*
* If LX_NO_ABORT_HANDLER has been set, we will
* not allow the emulated program to do
@@ -2182,10 +2174,6 @@ lx_siginit(void)
sigset_t new_set, oset;
int lx_sig, sig;
- if (getenv("LX_NO_ABORT_HANDLER") != NULL) {
- lx_no_abort_handler = 1;
- }
-
/*
* Block all signals possible while setting up the signal imposition
* mechanism.
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h
index 57692cfcd1..18e452876a 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h
@@ -24,7 +24,7 @@
*/
/*
- * Copyright (c) 2014 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#ifndef _LX_DEBUG_H
@@ -37,7 +37,7 @@ extern "C" {
#endif
/* initialize the debugging subsystem */
-extern void lx_debug_init(void);
+extern void lx_debug_init(boolean_t, boolean_t, const char *);
/* printf() style debug message functionality */
extern void lx_debug(const char *, ...);
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
index 35235887ad..7b2cf5eb7d 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h
@@ -51,6 +51,7 @@ extern char lx_release[LX_KERN_RELEASE_MAX];
extern char lx_cmd_name[MAXNAMLEN];
extern pid_t zoneinit_pid;
extern int lx_is_vforked;
+extern boolean_t lx_no_abort_handler;
/*
* Values Linux expects for init