diff options
author | bradchen@google.com <bradchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-27 23:50:40 +0000 |
---|---|---|
committer | bradchen@google.com <bradchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-27 23:50:40 +0000 |
commit | 5b99724ed67011e9ef4231ec2796a5237cdf8e88 (patch) | |
tree | f463db20eff57bcb29e32564610b0389597ea1a2 /chrome/nacl | |
parent | 95343df7917bc58e87e6455449fbfa2461ceb37c (diff) | |
download | chromium_src-5b99724ed67011e9ef4231ec2796a5237cdf8e88.zip chromium_src-5b99724ed67011e9ef4231ec2796a5237cdf8e88.tar.gz chromium_src-5b99724ed67011e9ef4231ec2796a5237cdf8e88.tar.bz2 |
New NaCl zygote implementation 2, in which Chrome zygote forks a NaCl helper.
This patch can launch earth_c.html with and without the SUID sandbox. It is enabled with the environment variable NACL_NEW_ZYGOTE.
BUG=nativeclient:480
TEST=nativeclient in-browser tests on Linux, ChromeOS
Review URL: http://codereview.chromium.org/6995121
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90681 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/nacl')
-rw-r--r-- | chrome/nacl/nacl_fork_delegate_linux.cc | 113 | ||||
-rw-r--r-- | chrome/nacl/nacl_helper_linux.cc | 161 | ||||
-rw-r--r-- | chrome/nacl/nacl_launcher_thread.h | 33 | ||||
-rw-r--r-- | chrome/nacl/nacl_listener.cc (renamed from chrome/nacl/nacl_launcher_thread.cc) | 62 | ||||
-rw-r--r-- | chrome/nacl/nacl_listener.h | 31 | ||||
-rw-r--r-- | chrome/nacl/nacl_main.cc | 10 |
6 files changed, 343 insertions, 67 deletions
diff --git a/chrome/nacl/nacl_fork_delegate_linux.cc b/chrome/nacl/nacl_fork_delegate_linux.cc new file mode 100644 index 0000000..57366b7f --- /dev/null +++ b/chrome/nacl/nacl_fork_delegate_linux.cc @@ -0,0 +1,113 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/nacl_fork_delegate_linux.h" + +#include <signal.h> +#include <stdlib.h> +#include <sys/socket.h> + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/file_path.h" +#include "base/process_util.h" +#include "content/common/unix_domain_socket_posix.h" +#include "content/common/zygote_fork_delegate_linux.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/nacl_helper_linux.h" + +NaClForkDelegate::NaClForkDelegate() : sandboxed_(false), + fd_(-1), + pid_(-1) { +} + +void NaClForkDelegate::Init(const bool sandboxed, + const int browserdesc, + const int sandboxdesc) { + VLOG(1) << "NaClForkDelegate::Init()"; + int fds[2]; + + sandboxed_ = sandboxed; + // Confirm a couple hard-wired assumptions. + // The NaCl constants are from chrome/nacl/nacl_linux_helper.h + DCHECK(kNaClBrowserDescriptor == browserdesc); + DCHECK(kNaClSandboxDescriptor == sandboxdesc); + + CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); + base::file_handle_mapping_vector fds_to_map; + fds_to_map.push_back(std::make_pair(fds[1], kNaClZygoteDescriptor)); + fds_to_map.push_back(std::make_pair(sandboxdesc, kNaClSandboxDescriptor)); + // TODO(bradchen): Before making this the default for release builds, + // replace command line switch with PathService::Get(). + const std::string nacl_zygote_exe = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kNaClLinuxHelper); + pid_ = -1; + if (nacl_zygote_exe.length() != 0) { + CommandLine::StringVector argv = CommandLine::ForCurrentProcess()->argv(); + argv[0] = nacl_zygote_exe; + base::LaunchAppWithClone(argv, fds_to_map, false, &pid_, + CLONE_FS | SIGCHLD); + // parent and error cases are handled below + } + HANDLE_EINTR(close(fds[1])); + if (pid_ > 0) { + const ssize_t kExpectedLength = strlen(kNaClHelperStartupAck); + char buf[kExpectedLength]; + + // Wait for ack from nacl_helper, indicating it is ready to help + const ssize_t nread = read(fds[0], buf, sizeof(buf)); + if (nread == kExpectedLength && + memcmp(buf, kNaClHelperStartupAck, nread) == 0) { + // all is well + fd_ = fds[0]; + return; + } + LOG(ERROR) << "Bad NaCl helper startup ack (" << nread << " bytes)"; + } + // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper + // becomes the default. + pid_ = -1; + fd_ = -1; + HANDLE_EINTR(close(fds[0])); +} + +NaClForkDelegate::~NaClForkDelegate() { + HANDLE_EINTR(close(fd_)); // side effect: delegate process will terminate +} + +bool NaClForkDelegate::CanHelp(const std::string& process_type) { + return (process_type == switches::kNaClLoaderProcess && pid_ != -1); +} + +pid_t NaClForkDelegate::Fork(const std::vector<int>& fds) { + base::ProcessId naclchild; + VLOG(1) << "NaClForkDelegate::Fork"; + + DCHECK(fds.size() == kNaClParentFDIndex + 1); + if (!UnixDomainSocket::SendMsg(fd_, kNaClForkRequest, + strlen(kNaClForkRequest), fds)) { + LOG(ERROR) << "NaClForkDelegate::Fork: SendMsg failed"; + return -1; + } + int nread = read(fd_, &naclchild, sizeof(naclchild)); + if (nread != sizeof(naclchild)) { + LOG(ERROR) << "NaClForkDelegate::Fork: read failed"; + return -1; + } + VLOG(1) << "nacl_child is " << naclchild << " (" << nread << " bytes)"; + return naclchild; +} + +bool NaClForkDelegate::AckChild(const int fd, + const std::string& channel_switch) { + int nwritten = HANDLE_EINTR(write(fd, channel_switch.c_str(), + channel_switch.length())); + if (nwritten != static_cast<int>(channel_switch.length())) { + return false; + } + return true; +} diff --git a/chrome/nacl/nacl_helper_linux.cc b/chrome/nacl/nacl_helper_linux.cc new file mode 100644 index 0000000..916c4da --- /dev/null +++ b/chrome/nacl/nacl_helper_linux.cc @@ -0,0 +1,161 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A mini-zygote specifically for Native Client. + +#include "chrome/common/nacl_helper_linux.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include <string> +#include <vector> + +#include "base/at_exit.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/rand_util.h" +#include "chrome/nacl/nacl_listener.h" +#include "content/common/main_function_params.h" +#include "content/common/unix_domain_socket_posix.h" +#include "ipc/ipc_switches.h" + +namespace { + +bool g_suid_sandbox_active; + +// The child must mimic the behavior of zygote_main_linux.cc on the child +// side of the fork. See zygote_main_linux.cc:HandleForkRequest from +// if (!child) { +// Note: this code doesn't attempt to support SELINUX or the SECCOMP sandbox. +void BecomeNaClLoader(const std::vector<int>& child_fds) { + VLOG(1) << "NaCl loader: setting up IPC descriptor"; + HANDLE_EINTR(close(kNaClZygoteDescriptor)); // don't need zygote FD any more + // Set up browser descriptor as expected by Chrome on fd 3 + // The zygote takes care of putting the sandbox IPC channel on fd 5 + int zfd = dup2(child_fds[kNaClBrowserFDIndex], kNaClBrowserDescriptor); + if (zfd != kNaClBrowserDescriptor) { + LOG(ERROR) << "Could not initialize kNaClBrowserDescriptor"; + _exit(-1); + } + + MessageLoopForIO main_message_loop; + NaClListener *listener = new NaClListener(); + listener->Listen(); + _exit(0); +} + +// Some of this code was lifted from +// content/browser/zygote_main_linux.cc:ForkWithRealPid() +void HandleForkRequest(const std::vector<int>& child_fds) { + VLOG(1) << "nacl_helper: forking"; + pid_t childpid = fork(); + if (childpid < 0) { + perror("fork"); + LOG(ERROR) << "*** HandleForkRequest failed\n"; + // fall through to parent case below + } else if (childpid == 0) { // In the child process. + bool validack = false; + const size_t kMaxReadSize = 1024; + char buffer[kMaxReadSize]; + // Wait until the parent process has discovered our PID. We + // should not fork any child processes (which the seccomp + // sandbox does) until then, because that can interfere with the + // parent's discovery of our PID. + const int nread = HANDLE_EINTR(read(child_fds[kNaClParentFDIndex], buffer, + kMaxReadSize)); + const std::string switch_prefix = std::string("--") + + switches::kProcessChannelID + std::string("="); + const size_t len = switch_prefix.length(); + + if (nread < 0) { + perror("read"); + LOG(ERROR) << "read returned " << nread; + } else if (nread > static_cast<int>(len)) { + if (switch_prefix.compare(0, len, buffer, 0, len) == 0) { + VLOG(1) << "NaCl loader is synchronised with Chrome zygote"; + CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kProcessChannelID, + std::string(&buffer[len], nread - len)); + validack = true; + } + } + HANDLE_EINTR(close(child_fds[kNaClDummyFDIndex])); + HANDLE_EINTR(close(child_fds[kNaClParentFDIndex])); + if (validack) { + BecomeNaClLoader(child_fds); + } else { + LOG(ERROR) << "Failed to synch with zygote"; + } + // NOTREACHED + return; + } + // I am the parent. + // First, close the dummy_fd so the sandbox won't find me when + // looking for the child's pid in /proc. Also close other fds. + for (size_t i = 0; i < child_fds.size(); i++) { + HANDLE_EINTR(close(child_fds[i])); + } + VLOG(1) << "nacl_helper: childpid is " << childpid; + // Now tell childpid to the Chrome zygote. + if (HANDLE_EINTR(send(kNaClZygoteDescriptor, + &childpid, sizeof(childpid), MSG_EOR)) + != sizeof(childpid)) { + LOG(ERROR) << "*** send() to zygote failed"; + } +} + +} // namespace + +int main(int argc, char *argv[]) { + CommandLine::Init(argc, argv); + base::AtExitManager exit_manager; + base::RandUint64(); // acquire /dev/urandom fd before sandbox is raised + std::vector<int> empty; // for SendMsg() calls + + g_suid_sandbox_active = (NULL != getenv("SBX_D")); + + // Send the zygote a message to let it know we are ready to help + if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, + kNaClHelperStartupAck, + sizeof(kNaClHelperStartupAck), empty)) { + LOG(ERROR) << "*** send() to zygote failed"; + } + + while (true) { + int badpid = -1; + std::vector<int> fds; + static const unsigned kMaxMessageLength = 2048; + char buf[kMaxMessageLength]; + const ssize_t msglen = UnixDomainSocket::RecvMsg(kNaClZygoteDescriptor, + &buf, sizeof(buf), &fds); + if (msglen == 0 || (msglen == -1 && errno == ECONNRESET)) { + // EOF from the browser. Goodbye! + _exit(0); + } + if (msglen == sizeof(kNaClForkRequest) - 1 && + memcmp(buf, kNaClForkRequest, msglen) == 0) { + if (kNaClParentFDIndex + 1 == fds.size()) { + HandleForkRequest(fds); + continue; // fork succeeded. Note: child does not return + } else { + LOG(ERROR) << "nacl_helper: unexpected number of fds, got " + << fds.size(); + } + } else { + if (msglen != 0) { + LOG(ERROR) << "nacl_helper unrecognized request: %s"; + _exit(-1); + } + } + // if fork fails, send PID=-1 to zygote + if (!UnixDomainSocket::SendMsg(kNaClZygoteDescriptor, &badpid, + sizeof(badpid), empty)) { + LOG(ERROR) << "*** send() to zygote failed"; + } + } +} diff --git a/chrome/nacl/nacl_launcher_thread.h b/chrome/nacl/nacl_launcher_thread.h deleted file mode 100644 index 4d709d6..0000000 --- a/chrome/nacl/nacl_launcher_thread.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_NACL_NACL_THREAD_H_ -#define CHROME_NACL_NACL_THREAD_H_ -#pragma once - -#include "base/native_library.h" -#include "chrome/common/nacl_types.h" -#include "content/common/child_thread.h" - -// The NaClLauncherThread class represents a background thread where -// NaCl app gets started. -class NaClLauncherThread : public ChildThread { - public: - explicit NaClLauncherThread(bool debug); - virtual ~NaClLauncherThread(); - // Returns the one NaCl thread. - static NaClLauncherThread* current(); - - private: - virtual bool OnControlMessageReceived(const IPC::Message& msg); - void OnStartSelLdr(std::vector<nacl::FileDescriptor> handles, - bool have_irt_file); - - int debug_enabled_; - - // TODO(gregoryd): do we need to override Cleanup as in PluginThread? - DISALLOW_COPY_AND_ASSIGN(NaClLauncherThread); -}; - -#endif // CHROME_NACL_NACL_THREAD_H_ diff --git a/chrome/nacl/nacl_launcher_thread.cc b/chrome/nacl/nacl_listener.cc index ad1c5e4..9652070 100644 --- a/chrome/nacl/nacl_launcher_thread.cc +++ b/chrome/nacl/nacl_listener.cc @@ -2,13 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/nacl/nacl_launcher_thread.h" +#include "chrome/nacl/nacl_listener.h" -#include <vector> +#include <errno.h> -#include "base/atomicops.h" +#include "base/command_line.h" +#include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" #include "chrome/common/nacl_messages.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_switches.h" #include "native_client/src/shared/imc/nacl_imc.h" #if defined(OS_LINUX) @@ -20,6 +24,15 @@ #include <io.h> #endif +// This is ugly. We need an interface header file for the exported +// sel_ldr interfaces. +// TODO(gregoryd,sehr): Add an interface header. +#if defined(OS_WIN) +typedef HANDLE NaClHandle; +#else +typedef int NaClHandle; +#endif // NaClHandle + #if defined(OS_MACOSX) namespace { @@ -56,43 +69,34 @@ int CreateMemoryObject(size_t size, bool executable) { } // namespace #endif // defined(OS_MACOSX) -// This is ugly. We need an interface header file for the exported -// sel_ldr interfaces. -// TODO(gregoryd,sehr): Add an interface header. -#if defined(OS_WIN) -typedef HANDLE NaClHandle; -#else -typedef int NaClHandle; -#endif // NaClHandle - -// This is currently necessary because we have a conflict between -// NaCl's LOG_FATAL (from platform/nacl_log.h) and Chromium's -// LOG_FATAL (from base/logging.h). -extern "C" int NaClMainForChromium(int handle_count, const NaClHandle* handles, +extern "C" int NaClMainForChromium(int handle_count, + const NaClHandle* handles, int debug); extern "C" void NaClSetIrtFileDesc(int fd); -NaClLauncherThread::NaClLauncherThread(bool debug) { - debug_enabled_ = debug ? 1 : 0; -} +NaClListener::NaClListener() {} -NaClLauncherThread::~NaClLauncherThread() { -} +NaClListener::~NaClListener() {} -NaClLauncherThread* NaClLauncherThread::current() { - return static_cast<NaClLauncherThread*>(ChildThread::current()); +void NaClListener::Listen() { + std::string channel_name = + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kProcessChannelID); + IPC::Channel channel(channel_name, IPC::Channel::MODE_CLIENT, this); + CHECK(channel.Connect()); + MessageLoop::current()->Run(); } -bool NaClLauncherThread::OnControlMessageReceived(const IPC::Message& msg) { +bool NaClListener::OnMessageReceived(const IPC::Message& msg) { bool handled = true; - IPC_BEGIN_MESSAGE_MAP(NaClLauncherThread, msg) - IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStartSelLdr) - IPC_MESSAGE_UNHANDLED(handled = false) + IPC_BEGIN_MESSAGE_MAP(NaClListener, msg) + IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStartSelLdr) + IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; } -void NaClLauncherThread::OnStartSelLdr( +void NaClListener::OnStartSelLdr( std::vector<nacl::FileDescriptor> handles, bool have_irt_file) { #if defined(OS_LINUX) @@ -127,5 +131,5 @@ void NaClLauncherThread::OnStartSelLdr( array[i] = nacl::ToNativeHandle(handles[i]); } NaClMainForChromium(static_cast<int>(handles.size()), array.get(), - debug_enabled_); + false /* debug */); } diff --git a/chrome/nacl/nacl_listener.h b/chrome/nacl/nacl_listener.h new file mode 100644 index 0000000..2434268 --- /dev/null +++ b/chrome/nacl/nacl_listener.h @@ -0,0 +1,31 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_NACL_NACL_LISTENER_H_ +#define CHROME_NACL_NACL_LISTENER_H_ +#pragma once + +#include <vector> + +#include "chrome/common/nacl_types.h" +#include "ipc/ipc_channel.h" + +// The NaClListener is an IPC channel listener that waits for a +// request to start a NaCl module. +class NaClListener : public IPC::Channel::Listener { + public: + NaClListener(); + ~NaClListener(); + // Listen for a request to launch a NaCl module. + void Listen(); + + private: + void OnStartSelLdr(std::vector<nacl::FileDescriptor> handles, + bool have_irt_file); + virtual bool OnMessageReceived(const IPC::Message& msg); + + DISALLOW_COPY_AND_ASSIGN(NaClListener); +}; + +#endif // CHROME_NACL_NACL_LISTENER_H_ diff --git a/chrome/nacl/nacl_main.cc b/chrome/nacl/nacl_main.cc index 64adb2c..e1564e2 100644 --- a/chrome/nacl/nacl_main.cc +++ b/chrome/nacl/nacl_main.cc @@ -15,13 +15,15 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/logging_chrome.h" -#include "chrome/nacl/nacl_launcher_thread.h" +#include "chrome/nacl/nacl_listener.h" #include "chrome/nacl/nacl_main_platform_delegate.h" #include "content/common/child_process.h" +#include "content/common/child_process_info.h" #include "content/common/hi_res_timer_manager.h" #include "content/common/main_function_params.h" #include "content/common/result_codes.h" #include "content/common/sandbox_policy.h" +#include "ipc/ipc_switches.h" #if defined(OS_WIN) #include "chrome/nacl/broker_thread.h" @@ -117,10 +119,8 @@ int NaClMain(const MainFunctionParams& parameters) { bool sandbox_test_result = platform.RunSandboxTests(); if (sandbox_test_result) { - ChildProcess nacl_process; - bool debug = parsed_command_line.HasSwitch(switches::kEnableNaClDebug); - nacl_process.set_main_thread(new NaClLauncherThread(debug)); - MessageLoop::current()->Run(); + NaClListener listener; + listener.Listen(); } else { // This indirectly prevents the test-harness-success-cookie from being set, // as a way of communicating test failure, because the nexe won't reply. |