Chromium Code Reviews| Index: content/zygote/zygote_main_linux.cc |
| diff --git a/content/zygote/zygote_main_linux.cc b/content/zygote/zygote_main_linux.cc |
| index b4da14eee8e726ba13cea0c3789e9a44d28b03e3..226cf78201db5de478e9291b61b9274399ad0eea 100644 |
| --- a/content/zygote/zygote_main_linux.cc |
| +++ b/content/zygote/zygote_main_linux.cc |
| @@ -5,8 +5,10 @@ |
| #include "content/zygote/zygote_main.h" |
| #include <dlfcn.h> |
| +#include <fcntl.h> |
| #include <pthread.h> |
| #include <string.h> |
| +#include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| @@ -17,6 +19,7 @@ |
| #include "base/memory/scoped_vector.h" |
| #include "base/native_library.h" |
| #include "base/pickle.h" |
| +#include "base/posix/eintr_wrapper.h" |
| #include "base/posix/unix_domain_socket_linux.h" |
| #include "base/rand_util.h" |
| #include "base/sys_info.h" |
| @@ -47,6 +50,10 @@ |
| #include "third_party/libjingle/overrides/init_webrtc.h" |
| #endif |
| +#if defined(ADDRESS_SANITIZER) |
| +#include <sanitizer/asan_interface.h> |
| +#endif |
| + |
| namespace content { |
| // See http://code.google.com/p/chromium/wiki/LinuxZygote |
| @@ -323,10 +330,11 @@ static void ZygotePreSandboxInit() { |
| new FontConfigIPC(GetSandboxFD()))->unref(); |
| } |
| -static bool CreateInitProcessReaper() { |
| +static bool CreateInitProcessReaper(base::Closure* post_fork_parent_callback) { |
| // The current process becomes init(1), this function returns from a |
| // newly created process. |
| - const bool init_created = sandbox::CreateInitProcessReaper(NULL); |
| + const bool init_created = |
| + sandbox::CreateInitProcessReaper(post_fork_parent_callback); |
| if (!init_created) { |
| LOG(ERROR) << "Error creating an init process to reap zombies"; |
| return false; |
| @@ -336,7 +344,8 @@ static bool CreateInitProcessReaper() { |
| // Enter the setuid sandbox. This requires the current process to have been |
| // created through the setuid sandbox. |
| -static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox) { |
| +static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox, |
| + base::Closure* post_fork_parent_callback) { |
| DCHECK(setuid_sandbox); |
| DCHECK(setuid_sandbox->IsSuidSandboxChild()); |
| @@ -364,7 +373,7 @@ static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox) { |
| if (getpid() == 1) { |
| // The setuid sandbox has created a new PID namespace and we need |
| // to assume the role of init. |
| - CHECK(CreateInitProcessReaper()); |
| + CHECK(CreateInitProcessReaper(post_fork_parent_callback)); |
| } |
| #if !defined(OS_OPENBSD) |
| @@ -398,10 +407,67 @@ static bool EnterSuidSandbox(sandbox::SetuidSandboxClient* setuid_sandbox) { |
| return true; |
| } |
| +#if defined(ADDRESS_SANITIZER) |
| +const size_t kSanitizerMaxMessageLength = 1 * 1024 * 1024; |
| + |
| +// A helper process which collects code coverage data from the renderers over a |
| +// socket and dumps it to a file. See http://crbug.com/336212 for discussion. |
| +static void SanitizerCoverageHelper(int socket_fd, int file_fd) { |
| + scoped_ptr<char[]> buffer(new char[kSanitizerMaxMessageLength]); |
| + while (true) { |
| + ssize_t received_size = HANDLE_EINTR( |
| + recv(socket_fd, buffer.get(), kSanitizerMaxMessageLength, 0)); |
| + PCHECK(received_size >= 0); |
| + if (received_size == 0) |
| + // All clients have closed the socket. We should die. |
| + _exit(0); |
| + PCHECK(file_fd >= 0); |
| + ssize_t written_size = 0; |
| + while (written_size < received_size) { |
| + ssize_t write_res = |
| + HANDLE_EINTR(write(file_fd, buffer.get() + written_size, |
| + received_size - written_size)); |
| + PCHECK(write_res >= 0); |
| + written_size += write_res; |
| + } |
| + PCHECK(0 == HANDLE_EINTR(fsync(file_fd))); |
| + } |
| +} |
| + |
| +// fds[0] is the read end, fds[1] is the write end. |
| +static void CreateSanitizerCoverageSocketPair(int fds[2]) { |
| + PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)); |
| + PCHECK(0 == shutdown(fds[0], SHUT_WR)); |
| + PCHECK(0 == shutdown(fds[1], SHUT_RD)); |
| +} |
| + |
| +static pid_t ForkSanitizerCoverageHelper(int child_fd, int parent_fd, |
| + base::ScopedFD file_fd) { |
| + pid_t pid = fork(); |
| + PCHECK(pid >= 0); |
| + if (pid == 0) { |
| + // In the child. |
| + PCHECK(0 == IGNORE_EINTR(close(parent_fd))); |
| + SanitizerCoverageHelper(child_fd, file_fd.get()); |
| + _exit(0); |
| + } else { |
| + // In the parent. |
| + PCHECK(0 == IGNORE_EINTR(close(child_fd))); |
| + return pid; |
| + } |
| +} |
| + |
| +void CloseFdPair(const int fds[2]) { |
| + PCHECK(0 == IGNORE_EINTR(close(fds[0]))); |
| + PCHECK(0 == IGNORE_EINTR(close(fds[1]))); |
| +} |
| +#endif // defined(ADDRESS_SANITIZER) |
| + |
| // If |is_suid_sandbox_child|, then make sure that the setuid sandbox is |
| // engaged. |
| static void EnterLayerOneSandbox(LinuxSandbox* linux_sandbox, |
| - bool is_suid_sandbox_child) { |
| + bool is_suid_sandbox_child, |
| + base::Closure* post_fork_parent_callback) { |
| DCHECK(linux_sandbox); |
| ZygotePreSandboxInit(); |
| @@ -415,7 +481,8 @@ static void EnterLayerOneSandbox(LinuxSandbox* linux_sandbox, |
| linux_sandbox->setuid_sandbox_client(); |
| if (is_suid_sandbox_child) { |
| - CHECK(EnterSuidSandbox(setuid_sandbox)) << "Failed to enter setuid sandbox"; |
| + CHECK(EnterSuidSandbox(setuid_sandbox, post_fork_parent_callback)) |
| + << "Failed to enter setuid sandbox"; |
| } |
| } |
| @@ -424,7 +491,26 @@ bool ZygoteMain(const MainFunctionParams& params, |
| g_am_zygote_or_renderer = true; |
| sandbox::InitLibcUrandomOverrides(); |
| + base::Closure *post_fork_parent_callback = NULL; |
| + |
| LinuxSandbox* linux_sandbox = LinuxSandbox::GetInstance(); |
| + |
| +#if defined(ADDRESS_SANITIZER) |
| + base::ScopedFD sancov_file_fd(__sanitizer_maybe_open_cov_file("zygote")); |
| + int sancov_socket_fds[2] = {-1, -1}; |
| + CreateSanitizerCoverageSocketPair(sancov_socket_fds); |
| + linux_sandbox->sanitizer_args()->coverage_sandboxed = 1; |
| + linux_sandbox->sanitizer_args()->coverage_fd = sancov_socket_fds[1]; |
| + linux_sandbox->sanitizer_args()->coverage_max_block_size = |
| + kSanitizerMaxMessageLength; |
| + // Zygote termination will block until the helper process exits, which will |
| + // not happen until the write end of the socket is closed everywhere. Make |
| + // sure the init process does not hold on to it. |
| + base::Closure close_sancov_socket_fds = |
| + base::Bind(&CloseFdPair, sancov_socket_fds); |
| + post_fork_parent_callback = &close_sancov_socket_fds; |
| +#endif |
| + |
| // This will pre-initialize the various sandboxes that need it. |
| linux_sandbox->PreinitializeSandbox(); |
| @@ -449,13 +535,31 @@ bool ZygoteMain(const MainFunctionParams& params, |
| } |
| // Turn on the first layer of the sandbox if the configuration warrants it. |
| - EnterLayerOneSandbox(linux_sandbox, must_enable_setuid_sandbox); |
| + EnterLayerOneSandbox(linux_sandbox, must_enable_setuid_sandbox, |
| + post_fork_parent_callback); |
| + |
| + std::vector<pid_t> extra_children; |
| + std::vector<int> extra_fds; |
| + |
| +#if defined(ADDRESS_SANITIZER) |
| + pid_t sancov_helper_pid = ForkSanitizerCoverageHelper( |
| + sancov_socket_fds[0], sancov_socket_fds[1], sancov_file_fd.Pass()); |
| + // It's important that the zygote reaps the helper before dying. Otherwise, |
| + // the destruction of the PID namespace could kill the helper before it |
| + // completes its I/O tasks. |
|
jln (very slow on Chromium)
2014/05/27 23:15:00
Let's add more information about the lifetime of s
earthdok
2014/05/28 16:44:51
Done.
|
| + extra_children.push_back(sancov_helper_pid); |
| + // Sanitizer code in the renderers will inherit the write end of the socket |
| + // from the zygote. We must keep it open until the very end of the zygote's |
| + // lifetime, even though we don't explicitly use it. |
| + extra_fds.push_back(sancov_socket_fds[1]); |
| +#endif |
| int sandbox_flags = linux_sandbox->GetStatus(); |
| bool setuid_sandbox_engaged = sandbox_flags & kSandboxLinuxSUID; |
| CHECK_EQ(must_enable_setuid_sandbox, setuid_sandbox_engaged); |
| - Zygote zygote(sandbox_flags, fork_delegates.Pass()); |
| + Zygote zygote(sandbox_flags, fork_delegates.Pass(), extra_children, |
| + extra_fds); |
| // This function call can return multiple times, once per fork(). |
| return zygote.ProcessRequests(); |
| } |