aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test
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/test
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/test')
-rw-r--r--erts/emulator/test/driver_SUITE.erl47
-rw-r--r--erts/emulator/test/driver_SUITE_data/Makefile.src3
-rw-r--r--erts/emulator/test/driver_SUITE_data/env_drv.c108
3 files changed, 157 insertions, 1 deletions
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index 475e03087a..884f697384 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -80,6 +80,7 @@
async_blast/1,
thr_msg_blast/1,
consume_timeslice/1,
+ env/1,
z_test/1]).
-export([bin_prefix/2]).
@@ -166,6 +167,7 @@ all() -> %% Keep a_test first and z_test last...
async_blast,
thr_msg_blast,
consume_timeslice,
+ env,
z_test].
groups() ->
@@ -2360,6 +2362,51 @@ count_proc_sched(Ps, PNs) ->
PNs
end.
+%%
+%% Tests whether erl_drv_putenv reflects in os:getenv and vice versa.
+%%
+env(Config) when is_list(Config) ->
+ ok = load_driver(proplists:get_value(data_dir, Config), env_drv),
+ Port = open_port({spawn_driver, env_drv}, []),
+ true = is_port(Port),
+
+ Keys = ["env_drv_a_key", "env_drv_b_key", "env_drv_c_key"],
+ Values = ["a_value", "b_value", "c_value"],
+
+ [env_put_test(Port, Key, Value) || Key <- Keys, Value <- Values],
+ [env_get_test(Port, Key, Value) || Key <- Keys, Value <- Values],
+ [env_oversize_test(Port, Key) || Key <- Keys],
+ [env_notfound_test(Port, Key) || Key <- Keys],
+
+ true = port_close(Port),
+ erl_ddll:unload_driver(env_drv),
+ ok.
+
+env_control(Port, Command, Key, Value) ->
+ KeyBin = list_to_binary(Key),
+ ValueBin = list_to_binary(Value),
+ Header = <<(byte_size(KeyBin)), (byte_size(ValueBin))>>,
+ Payload = <<KeyBin/binary, ValueBin/binary>>,
+ port_control(Port, Command, <<Header/binary, Payload/binary>>).
+
+env_put_test(Port, Key, Value) ->
+ os:unsetenv(Key),
+ [0] = env_control(Port, 0, Key, Value),
+ Value = os:getenv(Key).
+
+env_get_test(Port, Key, ExpectedValue) ->
+ true = os:putenv(Key, ExpectedValue),
+ [0] = env_control(Port, 1, Key, ExpectedValue).
+
+env_oversize_test(Port, Key) ->
+ os:putenv(Key, [$A || _ <- lists:seq(1, 1024)]),
+ [127] = env_control(Port, 1, Key, "").
+
+env_notfound_test(Port, Key) ->
+ true = os:unsetenv(Key),
+ [255] = env_control(Port, 1, Key, "").
+
+
a_test(Config) when is_list(Config) ->
rpc(Config, fun check_io_debug/0).
diff --git a/erts/emulator/test/driver_SUITE_data/Makefile.src b/erts/emulator/test/driver_SUITE_data/Makefile.src
index 1fedd72200..bcabaa689d 100644
--- a/erts/emulator/test/driver_SUITE_data/Makefile.src
+++ b/erts/emulator/test/driver_SUITE_data/Makefile.src
@@ -16,7 +16,8 @@ MISC_DRVS = outputv_drv@dll@ \
thr_free_drv@dll@ \
async_blast_drv@dll@ \
thr_msg_blast_drv@dll@ \
- consume_timeslice_drv@dll@
+ consume_timeslice_drv@dll@ \
+ env_drv@dll@
SYS_INFO_DRVS = sys_info_base_drv@dll@ \
sys_info_prev_drv@dll@ \
diff --git a/erts/emulator/test/driver_SUITE_data/env_drv.c b/erts/emulator/test/driver_SUITE_data/env_drv.c
new file mode 100644
index 0000000000..0e910eeb84
--- /dev/null
+++ b/erts/emulator/test/driver_SUITE_data/env_drv.c
@@ -0,0 +1,108 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2017. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/* Tests whether erl_drv_putenv/erl_drv_getenv work correctly and reflect
+ * changes to os:putenv/getenv. */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "erl_driver.h"
+
+static ErlDrvSSizeT env_drv_ctl(ErlDrvData drv_data, unsigned int cmd,
+ char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rsize);
+
+static ErlDrvEntry env_drv_entry = {
+ NULL /* init */,
+ NULL /* start */,
+ NULL /* stop */,
+ NULL /* output */,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "env_drv",
+ NULL /* finish */,
+ NULL /* handle */,
+ env_drv_ctl,
+ NULL /* timeout */,
+ NULL /* outputv*/,
+ NULL /* ready_async */,
+ NULL /* flush */,
+ NULL /* call*/,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */
+};
+
+DRIVER_INIT(env_drv) {
+ return &env_drv_entry;
+}
+
+static int test_putenv(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ char key[256], value[256];
+ int key_len, value_len;
+
+ key_len = buf[0];
+ value_len = buf[1];
+
+ sprintf(key, "%.*s", key_len, &buf[2]);
+ sprintf(value, "%.*s", value_len, &buf[2 + key_len]);
+
+ return erl_drv_putenv(key, value);
+}
+
+static int test_getenv(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) {
+ char expected_value[256], stored_value[256], key[256];
+ int expected_value_len, key_len;
+ size_t stored_value_len;
+ int res;
+
+ key_len = buf[0];
+ sprintf(key, "%.*s", key_len, &buf[2]);
+
+ expected_value_len = buf[1];
+ sprintf(expected_value, "%.*s", expected_value_len, &buf[2 + key_len]);
+
+ stored_value_len = sizeof(stored_value);
+ res = erl_drv_getenv(key, stored_value, &stored_value_len);
+
+ if(res == 0) {
+ return strcmp(stored_value, expected_value) != 0;
+ } else if(res == 1) {
+ return 127;
+ }
+
+ return 255;
+}
+
+static ErlDrvSSizeT env_drv_ctl(ErlDrvData drv_data, unsigned int cmd,
+ char* buf, ErlDrvSizeT len, char** rbuf, ErlDrvSizeT rsize) {
+
+ if(cmd == 0) {
+ (**rbuf) = (char)test_putenv(drv_data, buf, len);
+ } else {
+ (**rbuf) = (char)test_getenv(drv_data, buf, len);
+ }
+
+ return 1;
+}