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=b/30160149
TEST=make test-art-{host,target}-gtest-utils_test
Change-Id: Ice0097bf0de30db3d4ead124caf93316f39e3df3
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 6a50b8e..fb6c1b3 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -56,6 +56,22 @@
namespace art {
+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
+
#if defined(__linux__)
static constexpr bool kUseAddr2line = !kIsTargetBuild;
#endif
@@ -1393,6 +1409,15 @@
return filename;
}
+const EnvSnapshot* TakeEnvSnapshot() {
+ EnvSnapshot* snapshot = new EnvSnapshot();
+ char** env = GetEnviron();
+ for (size_t i = 0; env[i] != nullptr; ++i) {
+ snapshot->name_value_pairs_.emplace_back(new std::string(env[i]));
+ }
+ return snapshot;
+}
+
int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) {
const std::string command_line(Join(arg_vector, ' '));
CHECK_GE(arg_vector.size(), 1U) << command_line;
@@ -1416,8 +1441,20 @@
// 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 << ")";
+ // The child inherits the environment unless the caller overrides it.
+ if (Runtime::Current() == nullptr || Runtime::Current()->GetEnvSnapshot() == nullptr) {
+ execv(program, &args[0]);
+ } else {
+ const EnvSnapshot* saved_snapshot = Runtime::Current()->GetEnvSnapshot();
+ // Allocation between fork and exec is not well-behaved. Use a variable-length array instead.
+ char* envp[saved_snapshot->name_value_pairs_.size() + 1];
+ for (size_t i = 0; i < saved_snapshot->name_value_pairs_.size(); ++i) {
+ envp[i] = const_cast<char*>(saved_snapshot->name_value_pairs_[i]->c_str());
+ }
+ envp[saved_snapshot->name_value_pairs_.size()] = nullptr;
+ execve(program, &args[0], envp);
+ }
+ PLOG(ERROR) << "Failed to execve(" << command_line << ")";
// _exit to avoid atexit handlers in child.
_exit(1);
} else {