summaryrefslogtreecommitdiffstats
path: root/chrome/nacl
diff options
context:
space:
mode:
authorbradchen@google.com <bradchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-27 23:50:40 +0000
committerbradchen@google.com <bradchen@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-27 23:50:40 +0000
commit5b99724ed67011e9ef4231ec2796a5237cdf8e88 (patch)
treef463db20eff57bcb29e32564610b0389597ea1a2 /chrome/nacl
parent95343df7917bc58e87e6455449fbfa2461ceb37c (diff)
downloadchromium_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.cc113
-rw-r--r--chrome/nacl/nacl_helper_linux.cc161
-rw-r--r--chrome/nacl/nacl_launcher_thread.h33
-rw-r--r--chrome/nacl/nacl_listener.cc (renamed from chrome/nacl/nacl_launcher_thread.cc)62
-rw-r--r--chrome/nacl/nacl_listener.h31
-rw-r--r--chrome/nacl/nacl_main.cc10
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.