aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/sys/unix
diff options
context:
space:
mode:
authorJohn Högberg <[email protected]>2017-11-22 13:19:57 +0100
committerJohn Högberg <[email protected]>2018-01-03 08:14:28 +0100
commitc2d70945dce9cb09d5d7120d6e9ddf7faac8d230 (patch)
tree3cfbaca9af7b6a32871e669a39027ce987c846da /erts/emulator/sys/unix
parentbe9f93b14e0fb9ff09db36abde62ae8099bf5bd0 (diff)
downloadotp-c2d70945dce9cb09d5d7120d6e9ddf7faac8d230.tar.gz
otp-c2d70945dce9cb09d5d7120d6e9ddf7faac8d230.tar.bz2
otp-c2d70945dce9cb09d5d7120d6e9ddf7faac8d230.zip
Replace the libc environment with a thread-safe emulation
putenv(3) and friends aren't thread-safe regardless of how you slice it; a global lock around all environment operations (like before) keeps things safe as far as our own operations go, but we have absolutely no control over what libc or a library dragged in by a driver/NIF does -- they're free to call getenv(3) or putenv(3) without honoring our lock. This commit solves this by setting up an "emulated" environment which can't be touched without going through our interfaces. Third-party libraries can still shoot themselves in the foot but benign uses of os:putenv/2 will no longer risk crashing the emulator.
Diffstat (limited to 'erts/emulator/sys/unix')
-rw-r--r--erts/emulator/sys/unix/erl_unix_sys.h2
-rw-r--r--erts/emulator/sys/unix/sys.c127
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c183
-rw-r--r--erts/emulator/sys/unix/sys_env.c133
4 files changed, 197 insertions, 248 deletions
diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h
index b6f5b319ee..e367d565a7 100644
--- a/erts/emulator/sys/unix/erl_unix_sys.h
+++ b/erts/emulator/sys/unix/erl_unix_sys.h
@@ -133,7 +133,7 @@
#define ERTS_SYS_CONTINOUS_FD_NUMBERS
-typedef void *GETENV_STATE;
+void erts_sys_env_init(void);
/*
** For the erl_timer_sup module.
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 6315135151..189ca083d7 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -62,9 +62,6 @@
#include "erl_mseg.h"
-extern char **environ;
-erts_rwmtx_t environ_rwmtx;
-
#define MAX_VSIZE 16 /* Max number of entries allowed in an I/O
* vector sock_sendv().
*/
@@ -77,7 +74,7 @@ erts_rwmtx_t environ_rwmtx;
#include "erl_check_io.h"
#include "erl_cpu_topology.h"
-
+#include "erl_osenv.h"
extern int driver_interrupt(int, int);
extern void do_break(void);
@@ -454,10 +451,10 @@ prepare_crash_dump(int secs)
close(crashdump_companion_cube_fd);
envsz = sizeof(env);
- i = erts_sys_getenv__("ERL_CRASH_DUMP_NICE", env, &envsz);
+ i = erts_sys_explicit_8bit_getenv("ERL_CRASH_DUMP_NICE", env, &envsz);
if (i >= 0) {
int nice_val;
- nice_val = i != 0 ? 0 : atoi(env);
+ nice_val = i != 1 ? 0 : atoi(env);
if (nice_val > 39) {
nice_val = 39;
}
@@ -749,34 +746,6 @@ void os_version(int *pMajor, int *pMinor, int *pBuild) {
*pBuild = get_number(&release); /* Pointer to build number. */
}
-void init_getenv_state(GETENV_STATE *state)
-{
- erts_rwmtx_rlock(&environ_rwmtx);
- *state = NULL;
-}
-
-char *getenv_string(GETENV_STATE *state0)
-{
- char **state = (char **) *state0;
- char *cp;
-
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&environ_rwmtx));
-
- if (state == NULL)
- state = environ;
-
- cp = *state++;
- *state0 = (GETENV_STATE) state;
-
- return cp;
-}
-
-void fini_getenv_state(GETENV_STATE *state)
-{
- *state = NULL;
- erts_rwmtx_runlock(&environ_rwmtx);
-}
-
void erts_do_break_handling(void)
{
struct termios temp_mode;
@@ -830,90 +799,6 @@ void sys_get_pid(char *buffer, size_t buffer_size){
erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
-int
-erts_sys_putenv_raw(char *key, char *value) {
- return erts_sys_putenv(key, value);
-}
-int
-erts_sys_putenv(char *key, char *value)
-{
- int res;
- char *env;
- Uint need = strlen(key) + strlen(value) + 2;
-
-#ifdef HAVE_COPYING_PUTENV
- env = erts_alloc(ERTS_ALC_T_TMP, need);
-#else
- env = erts_alloc(ERTS_ALC_T_PUTENV_STR, need);
- erts_atomic_add_nob(&sys_misc_mem_sz, need);
-#endif
- strcpy(env,key);
- strcat(env,"=");
- strcat(env,value);
- erts_rwmtx_rwlock(&environ_rwmtx);
- res = putenv(env);
- erts_rwmtx_rwunlock(&environ_rwmtx);
-#ifdef HAVE_COPYING_PUTENV
- erts_free(ERTS_ALC_T_TMP, env);
-#endif
- return res;
-}
-
-int
-erts_sys_getenv__(char *key, char *value, size_t *size)
-{
- int res;
- char *orig_value = getenv(key);
- if (!orig_value)
- res = -1;
- else {
- size_t len = sys_strlen(orig_value);
- if (len >= *size) {
- *size = len + 1;
- res = 1;
- }
- else {
- *size = len;
- sys_memcpy((void *) value, (void *) orig_value, len+1);
- res = 0;
- }
- }
- return res;
-}
-
-int
-erts_sys_getenv_raw(char *key, char *value, size_t *size) {
- return erts_sys_getenv(key, value, size);
-}
-
-/*
- * erts_sys_getenv
- * returns:
- * -1, if environment key is not set with a value
- * 0, if environment key is set and value fits into buffer size
- * 1, if environment key is set but does not fit into buffer size
- * size is set with the needed buffer size value
- */
-
-int
-erts_sys_getenv(char *key, char *value, size_t *size)
-{
- int res;
- erts_rwmtx_rlock(&environ_rwmtx);
- res = erts_sys_getenv__(key, value, size);
- erts_rwmtx_runlock(&environ_rwmtx);
- return res;
-}
-
-int
-erts_sys_unsetenv(char *key)
-{
- int res;
- erts_rwmtx_rwlock(&environ_rwmtx);
- res = unsetenv(key);
- erts_rwmtx_rwunlock(&environ_rwmtx);
- return res;
-}
void sys_init_io(void) { }
void erts_sys_alloc_init(void) { }
@@ -1260,14 +1145,9 @@ erts_sys_main_thread(void)
}
}
-
void
erl_sys_args(int* argc, char** argv)
{
-
- erts_rwmtx_init(&environ_rwmtx, "environ", NIL,
- ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
-
ASSERT(argc && argv);
max_files = erts_check_io_max_files();
@@ -1275,4 +1155,5 @@ erl_sys_args(int* argc, char** argv)
init_smp_sig_notify();
init_smp_sig_suspend();
+ erts_sys_env_init();
}
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 0228e1af54..b7ac89d89a 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -55,9 +55,6 @@
#include "erl_threads.h"
-extern char **environ;
-extern erts_rwmtx_t environ_rwmtx;
-
extern erts_atomic_t sys_misc_mem_sz;
static Eterm forker_port;
@@ -180,7 +177,7 @@ erl_sys_late_init(void)
opts.read_write = 0;
opts.hide_window = 0;
opts.wd = NULL;
- opts.envir = NULL;
+ erts_osenv_init(&opts.envir);
opts.exit_status = 0;
opts.overlapped_io = 0;
opts.spawn_type = ERTS_SPAWN_ANY;
@@ -443,85 +440,55 @@ static void close_pipes(int ifd[2], int ofd[2])
close(ofd[1]);
}
-static char **build_unix_environment(char *block)
+struct __add_spawn_env_state {
+ struct iovec *iov;
+ int *iov_index;
+
+ Sint32 *payload_size;
+ char *env_block;
+};
+
+static void add_spawn_env_block_foreach(void *_state,
+ const erts_osenv_data_t *key,
+ const erts_osenv_data_t *value)
{
- int i;
- int j;
- int len;
- char *cp;
- char **cpp;
- char** old_env;
-
- ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&environ_rwmtx));
-
- cp = block;
- len = 0;
- while (*cp != '\0') {
- cp += strlen(cp) + 1;
- len++;
- }
- old_env = environ;
- while (*old_env++ != NULL) {
- len++;
- }
-
- cpp = (char **) erts_alloc_fnf(ERTS_ALC_T_ENVIRONMENT,
- sizeof(char *) * (len+1));
- if (cpp == NULL) {
- return NULL;
- }
+ struct __add_spawn_env_state *state;
+ struct iovec *iov;
- cp = block;
- len = 0;
- while (*cp != '\0') {
- cpp[len] = cp;
- cp += strlen(cp) + 1;
- len++;
- }
-
- i = len;
- for (old_env = environ; *old_env; old_env++) {
- char* old = *old_env;
-
- for (j = 0; j < len; j++) {
- char *s, *t;
-
- /* check if cpp[j] equals old
- before the = sign,
- i.e.
- "TMPDIR=/tmp/" */
- s = cpp[j];
- t = old;
- while (*s == *t && *s != '=') {
- s++, t++;
- }
- if (*s == '=' && *t == '=') {
- break;
- }
- }
+ state = (struct __add_spawn_env_state*)(_state);
+ iov = &state->iov[*state->iov_index];
- if (j == len) { /* New version not found */
- cpp[len++] = old;
- }
- }
+ iov->iov_base = state->env_block;
- for (j = 0; j < i; ) {
- size_t last = strlen(cpp[j])-1;
- if (cpp[j][last] == '=' && strchr(cpp[j], '=') == cpp[j]+last) {
- cpp[j] = cpp[--len];
- if (len < i) {
- i--;
- } else {
- j++;
- }
- }
- else {
- j++;
- }
- }
+ sys_memcpy(state->env_block, key->data, key->length);
+ state->env_block += key->length;
+ *state->env_block++ = '=';
+ sys_memcpy(state->env_block, value->data, value->length);
+ state->env_block += value->length;
+ *state->env_block++ = '\0';
- cpp[len] = NULL;
- return cpp;
+ iov->iov_len = state->env_block - (char*)iov->iov_base;
+
+ (*state->payload_size) += iov->iov_len;
+ (*state->iov_index)++;
+}
+
+static void *add_spawn_env_block(const erts_osenv_t *env, struct iovec *iov,
+ int *iov_index, Sint32 *payload_size) {
+ struct __add_spawn_env_state add_state;
+ char *env_block;
+
+ env_block = erts_alloc(ERTS_ALC_T_TMP, env->content_size +
+ env->variable_count * sizeof("=\0"));
+
+ add_state.iov = iov;
+ add_state.iov_index = iov_index;
+ add_state.env_block = env_block;
+ add_state.payload_size = payload_size;
+
+ erts_osenv_foreach_native(env, &add_state, add_spawn_env_block_foreach);
+
+ return env_block;
}
static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
@@ -531,7 +498,6 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
#define CMD_LINE_PREFIX_STR_SZ (sizeof(CMD_LINE_PREFIX_STR) - 1)
int len;
- char **new_environ;
ErtsSysDriverData *dd;
char *cmd_line;
char wd_buff[MAXPATHLEN+1];
@@ -598,19 +564,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
memcpy((void *) (cmd_line + CMD_LINE_PREFIX_STR_SZ), (void *) name, len);
cmd_line[CMD_LINE_PREFIX_STR_SZ + len] = '\0';
len = CMD_LINE_PREFIX_STR_SZ + len + 1;
- }
-
- erts_rwmtx_rlock(&environ_rwmtx);
-
- if (opts->envir == NULL) {
- new_environ = environ;
- } else if ((new_environ = build_unix_environment(opts->envir)) == NULL) {
- erts_rwmtx_runlock(&environ_rwmtx);
- close_pipes(ifd, ofd);
- erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- errno = ENOMEM;
- return ERL_DRV_ERROR_ERRNO;
- }
+}
if ((cwd = getcwd(wd_buff, MAXPATHLEN+1)) == NULL) {
/* on some OSs this call opens a fd in the
@@ -619,9 +573,6 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
int err = errno;
close_pipes(ifd, ofd);
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
- erts_rwmtx_runlock(&environ_rwmtx);
errno = err;
return ERL_DRV_ERROR_ERRNO;
}
@@ -629,6 +580,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
wd = opts->wd;
{
+ void *environment_block;
struct iovec *io_vector;
int iov_len = 5;
char nullbuff[] = "\0";
@@ -641,10 +593,8 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
if (wd) iov_len++;
- /* count number of elements in environment */
- while(new_environ[env_len] != NULL)
- env_len++;
- iov_len += 1 + env_len; /* num envs including size int */
+ /* num envs including size int */
+ iov_len += 1 + opts->envir.variable_count;
/* count number of element in argument list */
if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
@@ -661,10 +611,7 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
if (!io_vector) {
close_pipes(ifd, ofd);
- erts_rwmtx_runlock(&environ_rwmtx);
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
errno = ENOMEM;
return ERL_DRV_ERROR_ERRNO;
}
@@ -699,16 +646,13 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
io_vector[i++].iov_len = 1;
buffsz += io_vector[i-1].iov_len;
+ env_len = htonl(opts->envir.variable_count);
io_vector[i].iov_base = (void*)&env_len;
- env_len = htonl(env_len);
io_vector[i++].iov_len = sizeof(env_len);
buffsz += io_vector[i-1].iov_len;
- for (j = 0; new_environ[j] != NULL; j++) {
- io_vector[i].iov_base = new_environ[j];
- io_vector[i++].iov_len = strlen(new_environ[j]) + 1;
- buffsz += io_vector[i-1].iov_len;
- }
+ environment_block = add_spawn_env_block(&opts->envir, io_vector, &i,
+ &buffsz);
/* only append arguments if this was a spawn_executable */
if (opts->spawn_type == ERTS_SPAWN_EXECUTABLE) {
@@ -744,9 +688,6 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
int err = errno;
close_pipes(ifd, ofd);
erts_free(ERTS_ALC_T_TMP, io_vector);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
- erts_rwmtx_runlock(&environ_rwmtx);
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
errno = err;
return ERL_DRV_ERROR_ERRNO;
@@ -767,16 +708,12 @@ static ErlDrvData spawn_start(ErlDrvPort port_num, char* name,
driver_select(port_num, ofd[1], ERL_DRV_WRITE|ERL_DRV_USE, 1);
}
+ erts_free(ERTS_ALC_T_TMP, environment_block);
erts_free(ERTS_ALC_T_TMP, io_vector);
}
erts_free(ERTS_ALC_T_TMP, (void *) cmd_line);
- if (new_environ != environ)
- erts_free(ERTS_ALC_T_ENVIRONMENT, (void *) new_environ);
-
- erts_rwmtx_runlock(&environ_rwmtx);
-
dd = create_driver_data(port_num, ifd[0], ofd[1], opts->packet_bytes,
DO_WRITE | DO_READ, opts->exit_status,
0, 0);
@@ -1652,15 +1589,13 @@ static ErlDrvData forker_start(ErlDrvPort port_num, char* name,
forker_port = erts_drvport2id(port_num);
- res = erts_sys_getenv_raw("BINDIR", bindir, &bindirsz);
- if (res != 0) {
- if (res < 0)
- erts_exit(1,
- "Environment variable BINDIR is not set\n");
- if (res > 0)
- erts_exit(1,
- "Value of environment variable BINDIR is too large\n");
+ res = erts_sys_explicit_8bit_getenv("BINDIR", bindir, &bindirsz);
+ if (res == 0) {
+ erts_exit(1, "Environment variable BINDIR is not set\n");
+ } else if(res < 0) {
+ erts_exit(1, "Value of environment variable BINDIR is too large\n");
}
+
if (bindir[0] != DIR_SEPARATOR_CHAR)
erts_exit(1,
"Environment variable BINDIR does not contain an"
diff --git a/erts/emulator/sys/unix/sys_env.c b/erts/emulator/sys/unix/sys_env.c
new file mode 100644
index 0000000000..4d8301f985
--- /dev/null
+++ b/erts/emulator/sys/unix/sys_env.c
@@ -0,0 +1,133 @@
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_osenv.h"
+#include "erl_alloc.h"
+
+#include "erl_thr_progress.h"
+
+static erts_osenv_t sysenv_global_env;
+static erts_rwmtx_t sysenv_rwmtx;
+
+extern char **environ;
+
+static void import_initial_env(void);
+
+void erts_sys_env_init() {
+ erts_rwmtx_init(&sysenv_rwmtx, "environ", NIL,
+ ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
+
+ erts_osenv_init(&sysenv_global_env);
+ import_initial_env();
+}
+
+const erts_osenv_t *erts_sys_rlock_global_osenv() {
+ erts_rwmtx_rlock(&sysenv_rwmtx);
+ return &sysenv_global_env;
+}
+
+erts_osenv_t *erts_sys_rwlock_global_osenv() {
+ erts_rwmtx_rwlock(&sysenv_rwmtx);
+ return &sysenv_global_env;
+}
+
+void erts_sys_rwunlock_global_osenv() {
+ erts_rwmtx_rwunlock(&sysenv_rwmtx);
+}
+
+void erts_sys_runlock_global_osenv() {
+ erts_rwmtx_runlock(&sysenv_rwmtx);
+}
+
+int erts_sys_explicit_8bit_putenv(char *key, char *value) {
+ erts_osenv_data_t env_key, env_value;
+ int result;
+
+ env_key.length = sys_strlen(key);
+ env_key.data = key;
+
+ env_value.length = sys_strlen(value);
+ env_value.data = value;
+
+ {
+ erts_osenv_t *env = erts_sys_rwlock_global_osenv();
+ result = erts_osenv_put_native(env, &env_key, &env_value);
+ erts_sys_rwunlock_global_osenv();
+ }
+
+ return result;
+}
+
+int erts_sys_explicit_8bit_getenv(char *key, char *value, size_t *size) {
+ erts_osenv_data_t env_key, env_value;
+ int result;
+
+ env_key.length = sys_strlen(key);
+ env_key.data = key;
+
+ /* Reserve space for NUL termination. */
+ env_value.length = *size - 1;
+ env_value.data = value;
+
+ {
+ const erts_osenv_t *env = erts_sys_rlock_global_osenv();
+ result = erts_osenv_get_native(env, &env_key, &env_value);
+ erts_sys_runlock_global_osenv();
+ }
+
+ if(result == 1) {
+ value[env_value.length] = '\0';
+ }
+
+ *size = env_value.length;
+
+ return result;
+}
+
+int erts_sys_explicit_host_getenv(char *key, char *value, size_t *size) {
+ char *orig_value;
+ size_t length;
+
+ orig_value = getenv(key);
+
+ if(orig_value == NULL) {
+ return 0;
+ }
+
+ length = sys_strlen(orig_value);
+
+ if (length >= *size) {
+ *size = length + 1;
+ return -1;
+ }
+
+ sys_memcpy((void*)value, (void*)orig_value, length + 1);
+ *size = length;
+
+ return 1;
+}
+
+static void import_initial_env(void) {
+ char **environ_iterator, *environ_variable;
+
+ environ_iterator = environ;
+
+ while ((environ_variable = *(environ_iterator++)) != NULL) {
+ char *separator_index = strchr(environ_variable, '=');
+
+ if (separator_index != NULL) {
+ erts_osenv_data_t env_key, env_value;
+
+ env_key.length = separator_index - environ_variable;
+ env_key.data = environ_variable;
+
+ env_value.length = sys_strlen(separator_index) - 1;
+ env_value.data = separator_index + 1;
+
+ erts_osenv_put_native(&sysenv_global_env, &env_key, &env_value);
+ }
+ }
+}