diff options
-rw-r--r-- | runtime/oat_file_assistant_test.cc | 8 | ||||
-rw-r--r-- | runtime/runtime.cc | 41 | ||||
-rw-r--r-- | runtime/runtime.h | 20 | ||||
-rw-r--r-- | runtime/utils.cc | 11 | ||||
-rw-r--r-- | runtime/utils.h | 3 | ||||
-rw-r--r-- | runtime/utils_test.cc | 53 |
6 files changed, 130 insertions, 6 deletions
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index 05c5a22452..6ec5e55745 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -87,8 +87,10 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { 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 @@ class OatFileAssistantTest : public Dex2oatEnvironmentTest { 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 ddcfb6d5aa..f3fcd34de9 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 @@ struct TraceConfig { 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), @@ -904,6 +923,10 @@ void Runtime::SetSentinel(mirror::Object* sentinel) { } 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); @@ -2021,4 +2044,22 @@ bool Runtime::UseJitCompilation() const { return (jit_ != nullptr) && jit_->UseJitCompilation(); } +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 6da60f27a3..5f89d6adca 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -642,6 +642,12 @@ class Runtime { // 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(); @@ -864,6 +870,20 @@ class Runtime { // 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 b676ae5ae5..313190c84d 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1155,8 +1155,15 @@ int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_m // 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 693e0b87ee..843349277d 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -269,6 +269,9 @@ bool GetDalvikCacheFilename(const char* file_location, const char* cache_locatio 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 0a01cdb4ec..3ba20a4f02 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -16,6 +16,8 @@ #include "utils.h" +#include <stdlib.h> + #include "base/enums.h" #include "class_linker-inl.h" #include "common_runtime_test.h" @@ -380,8 +382,57 @@ TEST_F(UtilsTest, ExecError) { 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) { |