Index: base/process_util_posix.cc |
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc |
index cca07dd24f139a72973306af8dcd1c8a7e2cc603..5f8a76c96742103a47003dcc9f58a71608419bdb 100644 |
--- a/base/process_util_posix.cc |
+++ b/base/process_util_posix.cc |
@@ -18,6 +18,7 @@ |
#include "base/compiler_specific.h" |
#include "base/debug_util.h" |
+#include "base/dir_reader_posix.h" |
#include "base/eintr_wrapper.h" |
#include "base/logging.h" |
#include "base/platform_thread.h" |
@@ -28,8 +29,13 @@ |
#include "base/time.h" |
#include "base/waitable_event.h" |
+ |
#if defined(OS_MACOSX) |
+#include <crt_externs.h> |
+#define environ (*_NSGetEnviron()) |
#include "base/mach_ipc_mac.h" |
+#else |
+extern char** environ; |
#endif |
const int kMicrosecondsPerSecond = 1000000; |
@@ -173,24 +179,26 @@ class ScopedDIRClose { |
}; |
typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR; |
-void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { |
#if defined(OS_LINUX) |
static const rlim_t kSystemDefaultMaxFds = 8192; |
- static const char fd_dir[] = "/proc/self/fd"; |
+ static const char kFDDir[] = "/proc/self/fd"; |
#elif defined(OS_MACOSX) |
static const rlim_t kSystemDefaultMaxFds = 256; |
- static const char fd_dir[] = "/dev/fd"; |
+ static const char kFDDir[] = "/dev/fd"; |
#elif defined(OS_SOLARIS) |
static const rlim_t kSystemDefaultMaxFds = 8192; |
- static const char fd_dir[] = "/dev/fd"; |
+ static const char kFDDir[] = "/dev/fd"; |
#elif defined(OS_FREEBSD) |
static const rlim_t kSystemDefaultMaxFds = 8192; |
- static const char fd_dir[] = "/dev/fd"; |
+ static const char kFDDir[] = "/dev/fd"; |
#elif defined(OS_OPENBSD) |
static const rlim_t kSystemDefaultMaxFds = 256; |
- static const char fd_dir[] = "/dev/fd"; |
+ static const char kFDDir[] = "/dev/fd"; |
#endif |
- std::set<int> saved_fds; |
+ |
+void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { |
+ // DANGER: no calls to malloc are allowed from now on: |
+ // http://crbug.com/36678 |
// Get the maximum number of FDs possible. |
struct rlimit nofile; |
@@ -206,25 +214,20 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { |
if (max_fds > INT_MAX) |
max_fds = INT_MAX; |
- // Don't close stdin, stdout and stderr |
- saved_fds.insert(STDIN_FILENO); |
- saved_fds.insert(STDOUT_FILENO); |
- saved_fds.insert(STDERR_FILENO); |
- |
- for (base::InjectiveMultimap::const_iterator |
- i = saved_mapping.begin(); i != saved_mapping.end(); ++i) { |
- saved_fds.insert(i->dest); |
- } |
- |
- ScopedDIR dir_closer(opendir(fd_dir)); |
- DIR *dir = dir_closer.get(); |
- if (NULL == dir) { |
- DLOG(ERROR) << "Unable to open " << fd_dir; |
+ DirReaderPosix fd_dir(kFDDir); |
+ if (!fd_dir.IsValid()) { |
// Fallback case: Try every possible fd. |
for (rlim_t i = 0; i < max_fds; ++i) { |
const int fd = static_cast<int>(i); |
- if (saved_fds.find(fd) != saved_fds.end()) |
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) |
+ continue; |
+ InjectiveMultimap::const_iterator i; |
+ for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) { |
+ if (fd == i->dest) |
+ break; |
+ } |
+ if (i != saved_mapping.end()) |
continue; |
// Since we're just trying to close anything we can find, |
@@ -233,20 +236,27 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { |
} |
return; |
} |
- int dir_fd = dirfd(dir); |
- struct dirent *ent; |
- while ((ent = readdir(dir))) { |
+ const int dir_fd = fd_dir.fd(); |
+ |
+ for ( ; fd_dir.Next(); ) { |
// Skip . and .. entries. |
- if (ent->d_name[0] == '.') |
+ if (fd_dir.name()[0] == '.') |
continue; |
char *endptr; |
errno = 0; |
- const long int fd = strtol(ent->d_name, &endptr, 10); |
- if (ent->d_name[0] == 0 || *endptr || fd < 0 || errno) |
+ const long int fd = strtol(fd_dir.name(), &endptr, 10); |
+ if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno) |
+ continue; |
+ if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) |
continue; |
- if (saved_fds.find(fd) != saved_fds.end()) |
+ InjectiveMultimap::const_iterator i; |
+ for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) { |
+ if (fd == i->dest) |
+ break; |
+ } |
+ if (i != saved_mapping.end()) |
continue; |
if (fd == dir_fd) |
continue; |
@@ -262,40 +272,6 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { |
} |
} |
-// Sets all file descriptors to close on exec except for stdin, stdout |
-// and stderr. |
-// TODO(agl): Remove this function. It's fundamentally broken for multithreaded |
-// apps. |
-void SetAllFDsToCloseOnExec() { |
-#if defined(OS_LINUX) |
- const char fd_dir[] = "/proc/self/fd"; |
-#elif defined(OS_MACOSX) || defined(OS_FREEBSD) || defined(OS_SOLARIS) |
- const char fd_dir[] = "/dev/fd"; |
-#endif |
- ScopedDIR dir_closer(opendir(fd_dir)); |
- DIR *dir = dir_closer.get(); |
- if (NULL == dir) { |
- DLOG(ERROR) << "Unable to open " << fd_dir; |
- return; |
- } |
- |
- struct dirent *ent; |
- while ((ent = readdir(dir))) { |
- // Skip . and .. entries. |
- if (ent->d_name[0] == '.') |
- continue; |
- int i = atoi(ent->d_name); |
- // We don't close stdin, stdout or stderr. |
- if (i <= STDERR_FILENO) |
- continue; |
- |
- int flags = fcntl(i, F_GETFD); |
- if ((flags == -1) || (fcntl(i, F_SETFD, flags | FD_CLOEXEC) == -1)) { |
- DLOG(ERROR) << "fcntl failure."; |
- } |
- } |
-} |
- |
#if defined(OS_MACOSX) |
static std::string MachErrorCode(kern_return_t err) { |
return StringPrintf("0x%x %s", err, mach_error_string(err)); |
@@ -357,21 +333,107 @@ static pid_t fork_and_get_task(task_t* child_task) { |
} |
bool LaunchApp(const std::vector<std::string>& argv, |
- const environment_vector& environ, |
+ const environment_vector& env_changes, |
const file_handle_mapping_vector& fds_to_remap, |
bool wait, ProcessHandle* process_handle) { |
return LaunchAppAndGetTask( |
- argv, environ, fds_to_remap, wait, NULL, process_handle); |
+ argv, env_changes, fds_to_remap, wait, NULL, process_handle); |
} |
#endif // defined(OS_MACOSX) |
+// AlterEnvironment returns a modified environment vector, constructed from the |
+// current environment and the list of changes given in |changes|. Each key in |
+// the environment is matched against the first element of the pairs. In the |
+// event of a match, the value is replaced by the second of the pair, unless |
+// the second is empty, in which case the key-value is removed. |
+// |
+// The returned array is allocated using new[] and must be freed by the caller. |
+// Additionally, the first element is also allocated using new[] and should be |
+// freed by the caller. |
+static char** AlterEnvironment(const environment_vector& changes) { |
+ unsigned count = 0; |
+ unsigned size = 0; |
+ |
+ for (unsigned i = 0; environ[i]; i++) { |
+ const char *const pair = environ[i]; |
+ const char *const equals = strchr(pair, '='); |
+ if (!equals) { |
+ count++; |
+ size += strlen(pair) + 1 /* terminating NUL */; |
+ continue; |
+ } |
+ const unsigned keylen = equals - pair; |
+ bool handled = false; |
+ for (environment_vector::const_iterator |
+ j = changes.begin(); j != changes.end(); j++) { |
+ if (j->first.size() == keylen && |
+ memcmp(j->first.data(), pair, keylen) == 0) { |
+ if (!j->second.empty()) { |
+ count++; |
+ size += keylen + 1 /* '=' */ + j->second.size() + 1 /* NUL */; |
+ } |
+ handled = true; |
+ break; |
+ } |
+ } |
+ |
+ if (!handled) { |
+ count++; |
+ size += strlen(pair) + 1 /* terminating NUL */; |
+ } |
+ } |
+ |
+ char **const ret = new char*[count + 1 /* final NULL */]; |
+ unsigned k = 0; |
+ char *scratch = new char[size]; |
+ |
+ for (unsigned i = 0; environ[i]; i++) { |
+ const char *const pair = environ[i]; |
+ const char *const equals = strchr(pair, '='); |
+ if (!equals) { |
+ const unsigned len = strlen(pair); |
+ ret[k++] = scratch; |
+ memcpy(scratch, pair, len + 1); |
+ scratch += len + 1; |
+ continue; |
+ } |
+ const unsigned keylen = equals - pair; |
+ bool handled = false; |
+ for (environment_vector::const_iterator |
+ j = changes.begin(); j != changes.end(); j++) { |
+ if (j->first.size() == keylen && |
+ memcmp(j->first.data(), pair, keylen) == 0) { |
+ if (!j->second.empty()) { |
+ ret[k++] = scratch; |
+ memcpy(scratch, pair, keylen + 1); |
+ scratch += keylen + 1; |
+ memcpy(scratch, j->second.c_str(), j->second.size() + 1); |
+ scratch += j->second.size() + 1; |
+ } |
+ handled = true; |
+ break; |
+ } |
+ } |
+ |
+ if (!handled) { |
+ const unsigned len = strlen(pair); |
+ ret[k++] = scratch; |
+ memcpy(scratch, pair, len + 1); |
+ scratch += len + 1; |
+ } |
+ } |
+ |
+ ret[k] = NULL; |
+ return ret; |
+} |
+ |
#if defined(OS_MACOSX) |
bool LaunchAppAndGetTask( |
#else |
bool LaunchApp( |
#endif |
const std::vector<std::string>& argv, |
- const environment_vector& environ, |
+ const environment_vector& env_changes, |
const file_handle_mapping_vector& fds_to_remap, |
bool wait, |
#if defined(OS_MACOSX) |
@@ -379,6 +441,15 @@ bool LaunchApp( |
#endif |
ProcessHandle* process_handle) { |
pid_t pid; |
+ InjectiveMultimap fd_shuffle1, fd_shuffle2; |
+ fd_shuffle1.reserve(fds_to_remap.size()); |
+ fd_shuffle2.reserve(fds_to_remap.size()); |
+ scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); |
+ scoped_array<char*> new_environ(AlterEnvironment(env_changes)); |
+ // We need to free both the array of pointers, and the memory that they're |
+ // all pointing into. |
+ scoped_array<char> environ_buffer(new_environ[0]); |
+ |
#if defined(OS_MACOSX) |
if (task_handle == NULL) { |
pid = fork(); |
@@ -402,45 +473,44 @@ bool LaunchApp( |
RestoreDefaultExceptionHandler(); |
#endif |
- InjectiveMultimap fd_shuffle; |
+#if 0 |
+ // When debugging it can be helpful to check that we really aren't making |
+ // any hidden calls to malloc. |
+ void *malloc_thunk = |
+ reinterpret_cast<void*>(reinterpret_cast<intptr_t>(malloc) & ~4095); |
+ mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); |
+ memset(reinterpret_cast<void*>(malloc), 0xff, 8); |
+#endif |
+ |
+ // DANGER: no calls to malloc are allowed from now on: |
+ // http://crbug.com/36678 |
+ |
for (file_handle_mapping_vector::const_iterator |
it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { |
- fd_shuffle.push_back(InjectionArc(it->first, it->second, false)); |
+ fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); |
+ fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); |
} |
- for (environment_vector::const_iterator it = environ.begin(); |
- it != environ.end(); ++it) { |
- if (it->first.empty()) |
- continue; |
- |
- if (it->second.empty()) { |
- unsetenv(it->first.c_str()); |
- } else { |
- setenv(it->first.c_str(), it->second.c_str(), 1); |
- } |
- } |
+ environ = new_environ.get(); |
// Obscure fork() rule: in the child, if you don't end up doing exec*(), |
// you call _exit() instead of exit(). This is because _exit() does not |
// call any previously-registered (in the parent) exit handlers, which |
// might do things like block waiting for threads that don't even exist |
// in the child. |
- if (!ShuffleFileDescriptors(fd_shuffle)) |
- _exit(127); |
- // If we are using the SUID sandbox, it sets a magic environment variable |
- // ("SBX_D"), so we remove that variable from the environment here on the |
- // off chance that it's already set. |
- unsetenv("SBX_D"); |
+ // fd_shuffle1 is mutated by this call because it cannot malloc. |
+ if (!ShuffleFileDescriptors(&fd_shuffle1)) |
+ _exit(127); |
- CloseSuperfluousFds(fd_shuffle); |
+ CloseSuperfluousFds(fd_shuffle2); |
- scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); |
for (size_t i = 0; i < argv.size(); i++) |
argv_cstr[i] = const_cast<char*>(argv[i].c_str()); |
argv_cstr[argv.size()] = NULL; |
execvp(argv_cstr[0], argv_cstr.get()); |
- PLOG(ERROR) << "LaunchApp: execvp(" << argv_cstr[0] << ") failed"; |
+ RAW_LOG(ERROR, "LaunchApp: failed to execvp:"); |
+ RAW_LOG(ERROR, argv_cstr[0]); |
_exit(127); |
} else { |
// Parent process |
@@ -633,6 +703,12 @@ static bool GetAppOutputInternal(const CommandLine& cl, char* const envp[], |
bool do_search_path) { |
int pipe_fd[2]; |
pid_t pid; |
+ InjectiveMultimap fd_shuffle1, fd_shuffle2; |
+ const std::vector<std::string>& argv = cl.argv(); |
+ scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); |
+ |
+ fd_shuffle1.reserve(3); |
+ fd_shuffle2.reserve(3); |
// Either |do_search_path| should be false or |envp| should be null, but not |
// both. |
@@ -651,6 +727,8 @@ static bool GetAppOutputInternal(const CommandLine& cl, char* const envp[], |
#if defined(OS_MACOSX) |
RestoreDefaultExceptionHandler(); |
#endif |
+ // DANGER: no calls to malloc are allowed from now on: |
+ // http://crbug.com/36678 |
// Obscure fork() rule: in the child, if you don't end up doing exec*(), |
// you call _exit() instead of exit(). This is because _exit() does not |
@@ -661,18 +739,20 @@ static bool GetAppOutputInternal(const CommandLine& cl, char* const envp[], |
if (dev_null < 0) |
_exit(127); |
- InjectiveMultimap fd_shuffle; |
- fd_shuffle.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true)); |
- fd_shuffle.push_back(InjectionArc(dev_null, STDERR_FILENO, true)); |
- fd_shuffle.push_back(InjectionArc(dev_null, STDIN_FILENO, true)); |
+ fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true)); |
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true)); |
+ fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true)); |
+ // Adding another element here? Remeber to increase the argument to |
+ // reserve(), above. |
+ |
+ std::copy(fd_shuffle1.begin(), fd_shuffle1.end(), |
+ std::back_inserter(fd_shuffle2)); |
- if (!ShuffleFileDescriptors(fd_shuffle)) |
+ if (!ShuffleFileDescriptors(&fd_shuffle1)) |
_exit(127); |
- CloseSuperfluousFds(fd_shuffle); |
+ CloseSuperfluousFds(fd_shuffle2); |
- const std::vector<std::string> argv = cl.argv(); |
- scoped_array<char*> argv_cstr(new char*[argv.size() + 1]); |
for (size_t i = 0; i < argv.size(); i++) |
argv_cstr[i] = const_cast<char*>(argv[i].c_str()); |
argv_cstr[argv.size()] = NULL; |