Save environment snapshot and use at fork/exec

Some applications may inadvertently or maliciously set of environment
variables such as LD_LIBRARY_PATH before spawning subprocesses.
To make this more difficult, save the environment at the time the
runtime starts and use the saved copy anytime Exec is called.

BUG: 30160149
TEST: make test-art-{host,target}

Change-Id: I887b78bdb21ab20855636a96da14a74c767bbfef
(cherry picked from commit d106d9f871c957286ccdeb79c1c2a5ed41f859a6)
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 156d394..e99377d 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -87,8 +87,10 @@
                            bool with_patch_info = true) {
     // Temporarily redirect the dalvik cache so dex2oat doesn't find the
     // relocated image file.
-    std::string android_data_tmp = GetScratchDir() + "AndroidDataTmp";
-    setenv("ANDROID_DATA", android_data_tmp.c_str(), 1);
+    std::string dalvik_cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA));
+    std::string dalvik_cache_tmp = dalvik_cache + ".redirected";
+    ASSERT_EQ(0, rename(dalvik_cache.c_str(), dalvik_cache_tmp.c_str())) << strerror(errno);
+
     std::vector<std::string> args;
     args.push_back("--dex-file=" + dex_location);
     args.push_back("--oat-file=" + odex_location);
@@ -106,7 +108,7 @@
 
     std::string error_msg;
     ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-    setenv("ANDROID_DATA", android_data_.c_str(), 1);
+    ASSERT_EQ(0, rename(dalvik_cache_tmp.c_str(), dalvik_cache.c_str())) << strerror(errno);
 
     // Verify the odex file was generated as expected and really is
     // unrelocated.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 66c8f87..0cdda4e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -26,6 +26,9 @@
 #include <signal.h>
 #include <sys/syscall.h>
 #include "base/memory_tool.h"
+#if defined(__APPLE__)
+#include <crt_externs.h>  // for _NSGetEnviron
+#endif
 
 #include <cstdio>
 #include <cstdlib>
@@ -156,6 +159,22 @@
   size_t trace_file_size;
 };
 
+namespace {
+#ifdef __APPLE__
+inline char** GetEnviron() {
+  // When Google Test is built as a framework on MacOS X, the environ variable
+  // is unavailable. Apple's documentation (man environ) recommends using
+  // _NSGetEnviron() instead.
+  return *_NSGetEnviron();
+}
+#else
+// Some POSIX platforms expect you to declare environ. extern "C" makes
+// it reside in the global namespace.
+extern "C" char** environ;
+inline char** GetEnviron() { return environ; }
+#endif
+}  // namespace
+
 Runtime::Runtime()
     : resolution_method_(nullptr),
       imt_conflict_method_(nullptr),
@@ -905,6 +924,10 @@
 }
 
 bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
+  // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.
+  // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc.
+  env_snapshot_.TakeSnapshot();
+
   RuntimeArgumentMap runtime_options(std::move(runtime_options_in));
   ScopedTrace trace(__FUNCTION__);
   CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
@@ -2015,4 +2038,22 @@
   return (jit_ != nullptr) && jit_->SaveProfilingInfo();
 }
 
+void Runtime::EnvSnapshot::TakeSnapshot() {
+  char** env = GetEnviron();
+  for (size_t i = 0; env[i] != nullptr; ++i) {
+    name_value_pairs_.emplace_back(new std::string(env[i]));
+  }
+  // The strings in name_value_pairs_ retain ownership of the c_str, but we assign pointers
+  // for quick use by GetSnapshot.  This avoids allocation and copying cost at Exec.
+  c_env_vector_.reset(new char*[name_value_pairs_.size() + 1]);
+  for (size_t i = 0; env[i] != nullptr; ++i) {
+    c_env_vector_[i] = const_cast<char*>(name_value_pairs_[i]->c_str());
+  }
+  c_env_vector_[name_value_pairs_.size()] = nullptr;
+}
+
+char** Runtime::EnvSnapshot::GetSnapshot() const {
+  return c_env_vector_.get();
+}
+
 }  // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1394462..38680aa 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -648,6 +648,16 @@
     return zygote_no_threads_;
   }
 
+  // Returns if the code can be deoptimized. Code may be compiled with some
+  // optimization that makes it impossible to deoptimize.
+  bool IsDeoptimizeable(uintptr_t code) const SHARED_REQUIRES(Locks::mutator_lock_);
+
+  // Returns a saved copy of the environment (getenv/setenv values).
+  // Used by Fork to protect against overwriting LD_LIBRARY_PATH, etc.
+  char** GetEnvSnapshot() const {
+    return env_snapshot_.GetSnapshot();
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -872,6 +882,20 @@
   // Whether zygote code is in a section that should not start threads.
   bool zygote_no_threads_;
 
+  // Saved environment.
+  class EnvSnapshot {
+   public:
+    EnvSnapshot() = default;
+    void TakeSnapshot();
+    char** GetSnapshot() const;
+
+   private:
+    std::unique_ptr<char*[]> c_env_vector_;
+    std::vector<std::unique_ptr<std::string>> name_value_pairs_;
+
+    DISALLOW_COPY_AND_ASSIGN(EnvSnapshot);
+  } env_snapshot_;
+
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };
 std::ostream& operator<<(std::ostream& os, const Runtime::CalleeSaveType& rhs);
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 6a50b8e..0eb3a4d 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1416,8 +1416,15 @@
     // change process groups, so we don't get reaped by ProcessManager
     setpgid(0, 0);
 
-    execv(program, &args[0]);
-    PLOG(ERROR) << "Failed to execv(" << command_line << ")";
+    // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.
+    // Use the snapshot of the environment from the time the runtime was created.
+    char** envp = (Runtime::Current() == nullptr) ? nullptr : Runtime::Current()->GetEnvSnapshot();
+    if (envp == nullptr) {
+      execv(program, &args[0]);
+    } else {
+      execve(program, &args[0], envp);
+    }
+    PLOG(ERROR) << "Failed to execve(" << command_line << ")";
     // _exit to avoid atexit handlers in child.
     _exit(1);
   } else {
diff --git a/runtime/utils.h b/runtime/utils.h
index c1e88a4..fe915f2 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -291,6 +291,9 @@
 std::string GetSystemImageFilename(const char* location, InstructionSet isa);
 
 // Wrapper on fork/execv to run a command in a subprocess.
+// Both of these spawn child processes using the environment as it was set when the single instance
+// of the runtime (Runtime::Current()) was started.  If no instance of the runtime was started, it
+// will use the current environment settings.
 bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg);
 int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg);
 
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index f00edff..f7d9fba 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -16,6 +16,8 @@
 
 #include "utils.h"
 
+#include <stdlib.h>
+
 #include "class_linker-inl.h"
 #include "common_runtime_test.h"
 #include "mirror/array.h"
@@ -375,10 +377,59 @@
   if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
     // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
     EXPECT_FALSE(Exec(command, &error_msg));
-    EXPECT_NE(0U, error_msg.size());
+    EXPECT_FALSE(error_msg.empty());
   }
 }
 
+TEST_F(UtilsTest, EnvSnapshotAdditionsAreNotVisible) {
+  static constexpr const char* kModifiedVariable = "EXEC_SHOULD_NOT_EXPORT_THIS";
+  static constexpr int kOverwrite = 1;
+  // Set an variable in the current environment.
+  EXPECT_EQ(setenv(kModifiedVariable, "NEVER", kOverwrite), 0);
+  // Test that it is not exported.
+  std::vector<std::string> command;
+  if (kIsTargetBuild) {
+    std::string android_root(GetAndroidRoot());
+    command.push_back(android_root + "/bin/printenv");
+  } else {
+    command.push_back("/usr/bin/printenv");
+  }
+  command.push_back(kModifiedVariable);
+  std::string error_msg;
+  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    EXPECT_FALSE(Exec(command, &error_msg));
+    EXPECT_NE(0U, error_msg.size()) << error_msg;
+  }
+}
+
+TEST_F(UtilsTest, EnvSnapshotDeletionsAreNotVisible) {
+  static constexpr const char* kDeletedVariable = "PATH";
+  static constexpr int kOverwrite = 1;
+  // Save the variable's value.
+  const char* save_value = getenv(kDeletedVariable);
+  EXPECT_NE(save_value, nullptr);
+  // Delete the variable.
+  EXPECT_EQ(unsetenv(kDeletedVariable), 0);
+  // Test that it is not exported.
+  std::vector<std::string> command;
+  if (kIsTargetBuild) {
+    std::string android_root(GetAndroidRoot());
+    command.push_back(android_root + "/bin/printenv");
+  } else {
+    command.push_back("/usr/bin/printenv");
+  }
+  command.push_back(kDeletedVariable);
+  std::string error_msg;
+  if (!(RUNNING_ON_MEMORY_TOOL && kMemoryToolDetectsLeaks)) {
+    // Running on valgrind fails due to some memory that leaks in thread alternate signal stacks.
+    EXPECT_TRUE(Exec(command, &error_msg));
+    EXPECT_EQ(0U, error_msg.size()) << error_msg;
+  }
+  // Restore the variable's value.
+  EXPECT_EQ(setenv(kDeletedVariable, save_value, kOverwrite), 0);
+}
+
 TEST_F(UtilsTest, IsValidDescriptor) {
   std::vector<uint8_t> descriptor(
       { 'L', 'a', '/', 'b', '$', 0xed, 0xa0, 0x80, 0xed, 0xb0, 0x80, ';', 0x00 });