diff options
author | Patrick Mooney <pmooney@pfmooney.com> | 2016-06-24 23:03:34 +0000 |
---|---|---|
committer | Patrick Mooney <pmooney@pfmooney.com> | 2016-06-27 18:13:23 +0000 |
commit | 39bc219977fe06d26b768d7ddb2b73603a734446 (patch) | |
tree | 33875a15ff858d90d97b3fe3e9c03f20fbb3f81e | |
parent | 3071a4eca9e68f51208292bb7cab2fb922be4e48 (diff) | |
download | illumos-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.c | 53 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/debug.c | 22 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/lx_brand.c | 138 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/signal.c | 14 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h | 4 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h | 1 |
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(®, 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 |