Allow passing environment also for LD_PRELOAD

This expands support for passing an environment for the child process
also for the LD_PRELOAD case. The code now constructs the enviroment
for the child in a separate environment array for both code paths.
This also avoids messing with the parent's environment, which may have
unintended side effects.

BUG=chromium:1050997
TEST=New unit tests.

Change-Id: Ib05cad1d1ebe6e10d429501c8e467c3a53632753
diff --git a/util_unittest.cc b/util_unittest.cc
index c97dc2a..ab4805a 100644
--- a/util_unittest.cc
+++ b/util_unittest.cc
@@ -15,6 +15,20 @@
 
 #include "util.h"
 
+namespace {
+
+std::string dump_env(const char *const *env) {
+  std::string result;
+  for (; *env; ++env) {
+    result += *env;
+    result += "\n";
+  }
+
+  return result;
+}
+
+}  // namespace
+
 // Sanity check for the strip func.
 TEST(strip, basic) {
   char str[] = " foo\t";
@@ -81,3 +95,66 @@
   ASSERT_EQ(nullptr, p);
   ASSERT_EQ(nullptr, tokenize(&p, ","));
 }
+
+// Check environment manipulation functions.
+TEST(environment, copy_and_modify) {
+  minijail_free_env(nullptr);
+
+  char **env = minijail_copy_env(nullptr);
+  EXPECT_EQ("", dump_env(env));
+  minijail_free_env(env);
+
+  const char *const kConstEnv[] = {
+    "val1=1",
+    "val2=2",
+    "dup=1",
+    "dup=2",
+    "empty=",
+    nullptr,
+  };
+
+  // libc unfortunately uses char* const[] as the type for the environment, and
+  // we match that. It's actually missing a const-ness of the chars making up
+  // the environment strings, but we need that to initialize the |kEnv|
+  // constant. Hence, do a cast here to force things into alignment...
+  char* const* kEnv = const_cast<char* const*>(kConstEnv);
+
+  env = minijail_copy_env(kEnv);
+  EXPECT_EQ("val1=1\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env));
+  minijail_free_env(env);
+
+  env = minijail_copy_env(kEnv);
+  EXPECT_EQ("val1=1\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env));
+
+  EXPECT_EQ(EINVAL, minijail_setenv(nullptr, "val1", "3", 1));
+  char **env_ret = nullptr;
+  EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, "val1", "3", 1));
+
+  env_ret = env;
+  EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, nullptr, "3", 1));
+  EXPECT_EQ(env, env_ret);
+  EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, "", "3", 1));
+  EXPECT_EQ(env, env_ret);
+  EXPECT_EQ(EINVAL, minijail_setenv(&env_ret, "", nullptr, 1));
+  EXPECT_EQ(env, env_ret);
+
+  EXPECT_EQ(0, minijail_setenv(&env, "val1", "3", 0));
+  EXPECT_EQ("val1=1\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "val1", "3", 1));
+  EXPECT_EQ("val1=3\nval2=2\ndup=1\ndup=2\nempty=\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "val2", "4", 1));
+  EXPECT_EQ("val1=3\nval2=4\ndup=1\ndup=2\nempty=\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "dup", "5", 1));
+  EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "empty", "6", 1));
+  EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=6\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "empty", "", 1));
+  EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "new1", "7", 0));
+  EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\n", dump_env(env));
+  EXPECT_EQ(0, minijail_setenv(&env, "new2", "8", 1));
+  EXPECT_EQ("val1=3\nval2=4\ndup=5\ndup=2\nempty=\nnew1=7\nnew2=8\n",
+            dump_env(env));
+
+  minijail_free_env(env);
+}