diff options
-rw-r--r-- | base/process_util.h | 9 | ||||
-rw-r--r-- | base/process_util_posix.cc | 37 | ||||
-rw-r--r-- | chrome/app/chrome_main.cc | 12 | ||||
-rw-r--r-- | chrome/common/nacl_fork_delegate_linux.h | 37 | ||||
-rw-r--r-- | chrome/common/nacl_helper_linux.h | 46 | ||||
-rw-r--r-- | chrome/common/nacl_types.h | 6 | ||||
-rw-r--r-- | chrome/nacl.gypi | 24 | ||||
-rw-r--r-- | chrome/nacl/nacl_fork_delegate_linux.cc | 119 | ||||
-rw-r--r-- | chrome/nacl/nacl_helper_linux.cc | 167 | ||||
-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 | ||||
-rw-r--r-- | content/browser/child_process_launcher.cc | 6 | ||||
-rw-r--r-- | content/browser/zygote_host_linux.cc | 10 | ||||
-rw-r--r-- | content/browser/zygote_host_linux.h | 10 | ||||
-rw-r--r-- | content/browser/zygote_main_linux.cc | 108 | ||||
-rw-r--r-- | content/common/content_switches.cc | 4 | ||||
-rw-r--r-- | content/common/content_switches.h | 4 | ||||
-rw-r--r-- | content/common/sandbox_methods_linux.h | 4 | ||||
-rw-r--r-- | content/common/zygote_fork_delegate_linux.h | 49 |
21 files changed, 662 insertions, 126 deletions
diff --git a/base/process_util.h b/base/process_util.h index 3e758d4..9f66669 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -286,6 +286,15 @@ BASE_API bool LaunchAppInNewProcessGroup( bool wait, ProcessHandle* process_handle); +#if defined(OS_LINUX) +// Similar to LaunchApp variants above except uses clone(.. clone_flags ..) +// rather than fork(). This is useful for work inside the setuid sandbox. +BASE_API bool LaunchAppWithClone(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle, + int clone_flags); +#endif + // AlterEnvironment returns a modified environment vector, constructed from the // given 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 diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index a2398ed..1486128 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -508,15 +508,25 @@ bool LaunchAppImpl( const file_handle_mapping_vector& fds_to_remap, bool wait, ProcessHandle* process_handle, - bool start_new_process_group) { - pid_t pid; + bool start_new_process_group, + bool use_clone, + int clone_flags) { + pid_t pid = -1; 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, environ)); - pid = fork(); + if (use_clone) { +#if defined(OS_LINUX) + pid = syscall(__NR_clone, clone_flags, 0, 0, 0); +#else + NOTREACHED() << "Tried to use clone() on non-Linux system."; +#endif + } else { + pid = fork(); + } if (pid < 0) { PLOG(ERROR) << "fork"; return false; @@ -617,7 +627,10 @@ bool LaunchApp( bool wait, ProcessHandle* process_handle) { return LaunchAppImpl(argv, env_changes, fds_to_remap, - wait, process_handle, false); + wait, process_handle, + false, // don't start new process group + false, // don't use clone() + 0); // clone flags } bool LaunchAppInNewProcessGroup( @@ -627,7 +640,21 @@ bool LaunchAppInNewProcessGroup( bool wait, ProcessHandle* process_handle) { return LaunchAppImpl(argv, env_changes, fds_to_remap, wait, - process_handle, true); + process_handle, + true, // start new process group + false, // don't use clone() + 0); // clone flags +} + +BASE_API bool LaunchAppWithClone(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle, + int clone_flags) { + base::environment_vector no_env; + return LaunchAppImpl(argv, no_env, fds_to_remap, wait, process_handle, + false, // don't start new process group + true, // use clone() + clone_flags); } bool LaunchApp(const std::vector<std::string>& argv, diff --git a/chrome/app/chrome_main.cc b/chrome/app/chrome_main.cc index ed3827b..5130195 100644 --- a/chrome/app/chrome_main.cc +++ b/chrome/app/chrome_main.cc @@ -47,6 +47,11 @@ #include "ui/base/ui_base_paths.h" #include "ui/base/ui_base_switches.h" +#if defined(OS_POSIX) && !defined(OS_MACOSX) +#include "chrome/common/nacl_fork_delegate_linux.h" +#include "content/common/zygote_fork_delegate_linux.h" +#endif + #if defined(OS_WIN) #include <algorithm> #include <malloc.h> @@ -112,7 +117,9 @@ extern int WorkerMain(const MainFunctionParams&); extern int NaClMain(const MainFunctionParams&); extern int UtilityMain(const MainFunctionParams&); extern int ProfileImportMain(const MainFunctionParams&); -extern int ZygoteMain(const MainFunctionParams&); +#if defined(OS_POSIX) && !defined(OS_MACOSX) +extern int ZygoteMain(const MainFunctionParams&, const ZygoteForkDelegate&); +#endif #if defined(_WIN64) extern int NaClBrokerMain(const MainFunctionParams&); #endif @@ -450,7 +457,8 @@ int RunZygote(const MainFunctionParams& main_function_params) { media::InitializeMediaLibrary(media_path); // This function call can return multiple times, once per fork(). - if (!ZygoteMain(main_function_params)) + NaClForkDelegate nacl_fork_delegate; + if (!ZygoteMain(main_function_params, nacl_fork_delegate)) return 1; // Zygote::HandleForkRequest may have reallocated the command diff --git a/chrome/common/nacl_fork_delegate_linux.h b/chrome/common/nacl_fork_delegate_linux.h new file mode 100644 index 0000000..e84961e --- /dev/null +++ b/chrome/common/nacl_fork_delegate_linux.h @@ -0,0 +1,37 @@ +// 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_COMMON_NACL_FORK_DELEGATE_LINUX_H_ +#define CHROME_COMMON_NACL_FORK_DELEGATE_LINUX_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "content/common/zygote_fork_delegate_linux.h" + +// The NaClForkDelegate is created during Chrome linux zygote +// initialization, and provides "fork()" functionality with +// NaCl specific process characteristics (specifically address +// space layout) as an alternative to forking the zygote. +// A new delegate is passed in as an argument to ZygoteMain(). +class NaClForkDelegate : public ZygoteForkDelegate { + public: + NaClForkDelegate(); + virtual ~NaClForkDelegate(); + + virtual void Init(bool sandboxed, + int browserdesc, + int sandboxdesc) OVERRIDE; + virtual bool CanHelp(const std::string& process_type) OVERRIDE; + virtual pid_t Fork(const std::vector<int>& fds) OVERRIDE; + virtual bool AckChild(int fd, + const std::string& channel_switch) OVERRIDE; + + private: + bool sandboxed_; + int fd_; + pid_t pid_; +}; + +#endif // CHROME_COMMON_NACL_FORK_DELEGATE_LINUX_H_ diff --git a/chrome/common/nacl_helper_linux.h b/chrome/common/nacl_helper_linux.h new file mode 100644 index 0000000..a7e916d --- /dev/null +++ b/chrome/common/nacl_helper_linux.h @@ -0,0 +1,46 @@ +// 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_COMMON_NACL_HELPER_LINUX_H_ +#define CHROME_COMMON_NACL_HELPER_LINUX_H_ +#pragma once + +// A mini-zygote specifically for Native Client. This file defines +// constants used to implement communication between the nacl_helper +// process and the Chrome zygote. + +// Used by Helper to tell Zygote it has started successfully. +#define kNaClHelperStartupAck "NACLHELPER_OK" +// Used by Zygote to ask Helper to fork a new NaCl loader. +#define kNaClForkRequest "NACLFORK" + +// The next set of constants define global Linux file descriptors. +// For communications between NaCl loader and browser. +// See also content/common/zygote_main_linux.cc and +// http://code.google.com/p/chromium/wiki/LinuxZygote +#define kNaClBrowserDescriptor 3 +// For communications between NaCl loader and zygote. +// We put the kNaClZygoteDescriptor on 3 together with the +// kNaClBrowserDescriptor. They are never used at the same +// time, and this prevents /dev/urandom from using the fd. +#define kNaClZygoteDescriptor 3 +// For communications between the NaCl loader process and +// the SUID sandbox. +#define kNaClSandboxDescriptor 5 +// NOTE: kNaClBrowserDescriptor and kNaClSandboxDescriptor must match +// content/browser/zygote_main_linux.cc kBrowserDescriptor and +// kMagicSandboxIPCDescriptor. + +// A fork request from the Zygote to the helper includes an array +// of three file descriptors. These constants are used as indicies +// into the array. +// Used to pass in the descriptor for talking to the Browser +#define kNaClBrowserFDIndex 0 +// The next two are used in the protocol for discovering the +// child processes real PID from within the SUID sandbox. See +// http://code.google.com/p/chromium/wiki/LinuxZygote +#define kNaClDummyFDIndex 1 +#define kNaClParentFDIndex 2 + +#endif // CHROME_COMMON_NACL_HELPER_LINUX_H_ diff --git a/chrome/common/nacl_types.h b/chrome/common/nacl_types.h index b61d9ef..bcc1005 100644 --- a/chrome/common/nacl_types.h +++ b/chrome/common/nacl_types.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -7,6 +7,7 @@ #define CHROME_COMMON_NACL_TYPES_H_ #pragma once +#include "base/native_library.h" // for HANDLE and OS_POSIX #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" #endif @@ -19,7 +20,8 @@ namespace nacl { inline HANDLE ToNativeHandle(const FileDescriptor& desc) { return reinterpret_cast<HANDLE>(desc); } -#elif defined(OS_POSIX) +#endif +#if defined(OS_POSIX) typedef base::FileDescriptor FileDescriptor; inline int ToNativeHandle(const FileDescriptor& desc) { return desc.fd; diff --git a/chrome/nacl.gypi b/chrome/nacl.gypi index 9b7e6b4..21fbb01 100644 --- a/chrome/nacl.gypi +++ b/chrome/nacl.gypi @@ -28,8 +28,8 @@ 'nacl/nacl_main_platform_delegate_linux.cc', 'nacl/nacl_main_platform_delegate_mac.mm', 'nacl/nacl_main_platform_delegate_win.cc', - 'nacl/nacl_launcher_thread.cc', - 'nacl/nacl_launcher_thread.h', + 'nacl/nacl_listener.cc', + 'nacl/nacl_listener.h', ], # TODO(gregoryd): consider switching NaCl to use Chrome OS defines 'conditions': [ @@ -45,6 +45,9 @@ 'defines': [ '__STDC_LIMIT_MACROS=1', ], + 'sources': [ + 'nacl/nacl_fork_delegate_linux.cc', + ], },], ], }], @@ -141,5 +144,22 @@ }, ], }], + ['OS=="linux"', { + 'targets': [ + { + 'target_name': 'nacl_helper', + 'type': 'executable', + 'include_dirs': [ + '..', + ], + 'dependencies': [ + 'nacl', + ], + 'sources': [ + '../chrome/nacl/nacl_helper_linux.cc', + ], + }, + ], + }], ], } diff --git a/chrome/nacl/nacl_fork_delegate_linux.cc b/chrome/nacl/nacl_fork_delegate_linux.cc new file mode 100644 index 0000000..edc5c08 --- /dev/null +++ b/chrome/nacl/nacl_fork_delegate_linux.cc @@ -0,0 +1,119 @@ +// 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 <errno.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 + } + if (HANDLE_EINTR(close(fds[1])) < 0) + LOG(ERROR) << "close failed: " << errno; + + 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; + if (HANDLE_EINTR(close(fds[0])) < 0) + LOG(ERROR) << "close failed: " << errno; +} + +NaClForkDelegate::~NaClForkDelegate() { + // side effect of close: delegate process will terminate + if (HANDLE_EINTR(close(fd_)) < 0) + LOG(ERROR) << "close failed: " << errno; +} + +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..693f67f --- /dev/null +++ b/chrome/nacl/nacl_helper_linux.cc @@ -0,0 +1,167 @@ +// 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"; + if (HANDLE_EINTR(close(kNaClZygoteDescriptor)) < 0) + LOG(ERROR) << "close failed: " << errno; + + // 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; + } + } + if (HANDLE_EINTR(close(child_fds[kNaClDummyFDIndex]))) + LOG(ERROR) << "close failed: " << errno; + if (HANDLE_EINTR(close(child_fds[kNaClParentFDIndex]))) + LOG(ERROR) << "close failed: " << errno; + + 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++) { + if (HANDLE_EINTR(close(child_fds[i]))) + LOG(ERROR) << "close failed: " << errno; + } + 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..c326c95 --- /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(); + virtual ~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. diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc index 8d0ac12..3ba9d8a 100644 --- a/content/browser/child_process_launcher.cc +++ b/content/browser/child_process_launcher.cc @@ -127,8 +127,9 @@ class ChildProcessLauncher::Context mapping.push_back(std::pair<uint32_t, int>(kCrashDumpSignal, crash_signal_fd)); } - handle = ZygoteHost::GetInstance()->ForkRenderer(cmd_line->argv(), - mapping); + handle = ZygoteHost::GetInstance()->ForkRequest(cmd_line->argv(), + mapping, + process_type); } else // Fall through to the normal posix case below when we're not zygoting. #endif @@ -374,4 +375,3 @@ void ChildProcessLauncher::SetTerminateChildOnShutdown( if (context_) context_->set_terminate_child_on_shutdown(terminate_on_shutdown); } - diff --git a/content/browser/zygote_host_linux.cc b/content/browser/zygote_host_linux.cc index 328c603..16cc514 100644 --- a/content/browser/zygote_host_linux.cc +++ b/content/browser/zygote_host_linux.cc @@ -58,8 +58,7 @@ ZygoteHost::ZygoteHost() init_(false), using_suid_sandbox_(false), have_read_sandbox_status_word_(false), - sandbox_status_(0) { -} + sandbox_status_(0) {} ZygoteHost::~ZygoteHost() { if (init_) @@ -110,6 +109,7 @@ void ZygoteHost::Init(const std::string& sandbox_cmd) { switches::kRegisterPepperPlugins, switches::kDisableSeccompSandbox, switches::kEnableSeccompSandbox, + switches::kNaClLinuxHelper, }; cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, arraysize(kForwardSwitches)); @@ -221,13 +221,15 @@ ssize_t ZygoteHost::ReadReply(void* buf, size_t buf_len) { return HANDLE_EINTR(read(control_fd_, buf, buf_len)); } -pid_t ZygoteHost::ForkRenderer( +pid_t ZygoteHost::ForkRequest( const std::vector<std::string>& argv, - const base::GlobalDescriptors::Mapping& mapping) { + const base::GlobalDescriptors::Mapping& mapping, + const std::string& process_type) { DCHECK(init_); Pickle pickle; pickle.WriteInt(kCmdFork); + pickle.WriteString(process_type); pickle.WriteInt(argv.size()); for (std::vector<std::string>::const_iterator i = argv.begin(); i != argv.end(); ++i) diff --git a/content/browser/zygote_host_linux.h b/content/browser/zygote_host_linux.h index 5ead5f5..9098e6d 100644 --- a/content/browser/zygote_host_linux.h +++ b/content/browser/zygote_host_linux.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -32,10 +32,12 @@ class ZygoteHost { void Init(const std::string& sandbox_cmd); - // Tries to start a renderer process. Returns its pid on success, otherwise + // Tries to start a process of type indicated by process_type. + // Returns its pid on success, otherwise // base::kNullProcessHandle; - pid_t ForkRenderer(const std::vector<std::string>& command_line, - const base::GlobalDescriptors::Mapping& mapping); + pid_t ForkRequest(const std::vector<std::string>& command_line, + const base::GlobalDescriptors::Mapping& mapping, + const std::string& process_type); void EnsureProcessTerminated(pid_t process); // Get the termination status (and, optionally, the exit code) of diff --git a/content/browser/zygote_main_linux.cc b/content/browser/zygote_main_linux.cc index 99a7735..477ca90 100644 --- a/content/browser/zygote_main_linux.cc +++ b/content/browser/zygote_main_linux.cc @@ -37,9 +37,11 @@ #include "content/common/sandbox_methods_linux.h" #include "content/common/set_process_title.h" #include "content/common/unix_domain_socket_posix.h" +#include "content/common/zygote_fork_delegate_linux.h" #include "seccompsandbox/sandbox.h" #include "skia/ext/SkFontHost_fontconfig_control.h" #include "unicode/timezone.h" +#include "ipc/ipc_switches.h" #if defined(OS_LINUX) #include <sys/epoll.h> @@ -97,8 +99,9 @@ static void SELinuxTransitionToTypeOrDie(const char* type) { // runs it. class Zygote { public: - explicit Zygote(int sandbox_flags) - : sandbox_flags_(sandbox_flags) { + explicit Zygote(int sandbox_flags, ZygoteForkDelegate& helper) + : sandbox_flags_(sandbox_flags), + helper_(helper) { } bool ProcessRequests() { @@ -165,6 +168,7 @@ class Zygote { case ZygoteHost::kCmdFork: // This function call can return multiple times, once per fork(). return HandleForkRequest(fd, pickle, iter, fds); + case ZygoteHost::kCmdReap: if (!fds.empty()) break; @@ -247,9 +251,12 @@ class Zygote { // sandbox, it returns the real PID of the child process as it // appears outside the sandbox, rather than returning the PID inside // the sandbox. - int ForkWithRealPid() { - if (!g_suid_sandbox_active) + int ForkWithRealPid(const std::string& process_type, std::vector<int>& fds, + const std::string& channel_switch) { + const bool use_helper = helper_.CanHelp(process_type); + if (!(use_helper || g_suid_sandbox_active)) { return fork(); + } int dummy_fd; ino_t dummy_inode; @@ -270,7 +277,13 @@ class Zygote { goto error; } - pid = fork(); + if (use_helper) { + fds.push_back(dummy_fd); + fds.push_back(pipe_fds[0]); + pid = helper_.Fork(fds); + } else { + pid = fork(); + } if (pid < 0) { goto error; } else if (pid == 0) { @@ -294,33 +307,43 @@ class Zygote { dummy_fd = -1; close(pipe_fds[0]); pipe_fds[0] = -1; - uint8_t reply_buf[512]; - Pickle request; - request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); - request.WriteUInt64(dummy_inode); - - const ssize_t r = UnixDomainSocket::SendRecvMsg( - kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, - request); - if (r == -1) { - LOG(ERROR) << "Failed to get child process's real PID"; - goto error; - } - base::ProcessId real_pid; - Pickle reply(reinterpret_cast<char*>(reply_buf), r); - void* iter2 = NULL; - if (!reply.ReadInt(&iter2, &real_pid)) - goto error; - if (real_pid <= 0) { - // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already? - LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed"; - goto error; + if (g_suid_sandbox_active) { + uint8_t reply_buf[512]; + Pickle request; + request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); + request.WriteUInt64(dummy_inode); + + const ssize_t r = UnixDomainSocket::SendRecvMsg( + kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, + request); + if (r == -1) { + LOG(ERROR) << "Failed to get child process's real PID"; + goto error; + } + + Pickle reply(reinterpret_cast<char*>(reply_buf), r); + void* iter = NULL; + if (!reply.ReadInt(&iter, &real_pid)) + goto error; + if (real_pid <= 0) { + // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already? + LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed"; + goto error; + } + real_pids_to_sandbox_pids[real_pid] = pid; } - real_pids_to_sandbox_pids[real_pid] = pid; - if (HANDLE_EINTR(write(pipe_fds[1], "x", 1)) != 1) { - LOG(ERROR) << "Failed to synchronise with child process"; - goto error; + if (use_helper) { + real_pid = pid; + if (!helper_.AckChild(pipe_fds[1], channel_switch)) { + LOG(ERROR) << "Failed to synchronise with NaCl child process"; + goto error; + } + } else { + if (HANDLE_EINTR(write(pipe_fds[1], "x", 1)) != 1) { + LOG(ERROR) << "Failed to synchronise with child process"; + goto error; + } } close(pipe_fds[1]); return real_pid; @@ -342,12 +365,19 @@ class Zygote { // Handle a 'fork' request from the browser: this means that the browser // wishes to start a new renderer. - bool HandleForkRequest(int fd, const Pickle& pickle, void* iter, - std::vector<int>& fds) { + bool HandleForkRequest(int fd, const Pickle& pickle, + void* iter, std::vector<int>& fds) { std::vector<std::string> args; int argc, numfds; base::GlobalDescriptors::Mapping mapping; base::ProcessId child; + std::string process_type; + std::string channel_id; + const std::string channel_id_prefix = std::string("--") + + switches::kProcessChannelID + std::string("="); + + if (!pickle.ReadString(&iter, &process_type)) + goto error; if (!pickle.ReadInt(&iter, &argc)) goto error; @@ -357,6 +387,8 @@ class Zygote { if (!pickle.ReadString(&iter, &arg)) goto error; args.push_back(arg); + if (arg.compare(0, channel_id_prefix.length(), channel_id_prefix) == 0) + channel_id = arg; } if (!pickle.ReadInt(&iter, &numfds)) @@ -374,7 +406,7 @@ class Zygote { mapping.push_back(std::make_pair( static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor)); - child = ForkWithRealPid(); + child = ForkWithRealPid(process_type, fds, channel_id); if (!child) { #if defined(SECCOMP_SANDBOX) @@ -447,6 +479,7 @@ class Zygote { ProcessMap real_pids_to_sandbox_pids; const int sandbox_flags_; + ZygoteForkDelegate& helper_; }; // With SELinux we can carve out a precise sandbox, so we don't have to play @@ -705,7 +738,8 @@ static bool EnterSandbox() { #endif // CHROMIUM_SELINUX -bool ZygoteMain(const MainFunctionParams& params) { +bool ZygoteMain(const MainFunctionParams& params, + const ZygoteForkDelegate& forkdelegate) { #if !defined(CHROMIUM_SELINUX) g_am_zygote_or_renderer = true; #endif @@ -724,6 +758,10 @@ bool ZygoteMain(const MainFunctionParams& params) { } #endif // SECCOMP_SANDBOX + VLOG(1) << "initializing fork delegate"; + forkdelegate.Init(getenv("SBX_D") != NULL, // g_suid_sandbox_active, + kBrowserDescriptor, kMagicSandboxIPCDescriptor); + // Turn on the SELinux or SUID sandbox if (!EnterSandbox()) { LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " @@ -760,7 +798,7 @@ bool ZygoteMain(const MainFunctionParams& params) { } #endif // SECCOMP_SANDBOX - Zygote zygote(sandbox_flags); + Zygote zygote(sandbox_flags, forkdelegate); // This function call can return multiple times, once per fork(). return zygote.ProcessRequests(); } diff --git a/content/common/content_switches.cc b/content/common/content_switches.cc index 7033c7f..3e22610 100644 --- a/content/common/content_switches.cc +++ b/content/common/content_switches.cc @@ -274,6 +274,10 @@ const char kLogPluginMessages[] = "log-plugin-messages"; // (used for launching NaCl loader processes on 64-bit Windows). const char kNaClBrokerProcess[] = "nacl-broker"; +// Enables experimental lightweight Native Client launcher for Linux +// Value is the path to the helper binary. +const char kNaClLinuxHelper[] = "nacl-linux-helper"; + // Causes the process to run as a NativeClient loader. const char kNaClLoaderProcess[] = "nacl-loader"; diff --git a/content/common/content_switches.h b/content/common/content_switches.h index f48e69a..1b2384f 100644 --- a/content/common/content_switches.h +++ b/content/common/content_switches.h @@ -92,6 +92,10 @@ extern const char kLogPluginMessages[]; // TODO(jam): this doesn't belong in content. extern const char kNaClBrokerProcess[]; extern const char kNaClLoaderProcess[]; +// TODO(bradchen): remove kNaClLinuxHelper switch. +// This switch enables the experimental lightweight nacl_helper for Linux. +// It will be going away soon, when the helper is enabled permanently. +extern const char kNaClLinuxHelper[]; extern const char kNoDisplayingInsecureContent[]; extern const char kNoJsRandomness[]; extern const char kNoReferrers[]; diff --git a/content/common/sandbox_methods_linux.h b/content/common/sandbox_methods_linux.h index 02287f6d..f048ad8 100644 --- a/content/common/sandbox_methods_linux.h +++ b/content/common/sandbox_methods_linux.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// 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. @@ -7,7 +7,7 @@ #pragma once // This is a list of sandbox IPC methods which the renderer may send to the -// sandbox host. See http://code.google.com/p/chromium/LinuxSandboxIPC +// sandbox host. See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC // This isn't the full list, values < 32 are reserved for methods called from // Skia. class LinuxSandbox { diff --git a/content/common/zygote_fork_delegate_linux.h b/content/common/zygote_fork_delegate_linux.h new file mode 100644 index 0000000..26dfbc9 --- /dev/null +++ b/content/common/zygote_fork_delegate_linux.h @@ -0,0 +1,49 @@ +// 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 CONTENT_COMMON_ZYGOTE_FORK_DELEGATE_LINUX_H_ +#define CONTENT_COMMON_ZYGOTE_FORK_DELEGATE_LINUX_H_ +#pragma once + +#include <unistd.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" + +// The ZygoteForkDelegate allows the Chrome Linux zygote to delegate +// fork operations to another class that knows how to do some +// specialized version of fork. +class ZygoteForkDelegate { + public: + // A ZygoteForkDelegate is created during Chrome linux zygote + // initialization, and provides "fork()" functionality with + // as an alternative to forking the zygote. A new delegate is + // passed in as an argument to ZygoteMain(). + ZygoteForkDelegate() {} + virtual ~ZygoteForkDelegate() {} + + // Initialization happens in the zygote after it has been + // started by ZygoteMain. + virtual void Init(bool sandboxed, + int browserdesc, + int sandboxdesc) = 0; + + // Returns 'true' if the delegate would like to handle a given + // fork request. Otherwise returns false. + virtual bool CanHelp(const std::string& process_type) = 0; + + // Delegate forks, returning a -1 on failure. Outside the + // suid sandbox, Fork() returns the Linux process ID. Inside + // the sandbox, returns a positive integer, with PID discovery + // handled by the sandbox. + virtual pid_t Fork(const std::vector<int>& fds) = 0; + + // After a successful for, signal the child to indicate that + // the child's PID has been received. Also communicate the + // channel switch as a part of acknowledgement message. + virtual bool AckChild(int fd, const std::string& channel_switch) = 0; +}; +#endif // CONTENT_COMMON_ZYGOTE_FORK_DELEGATE_LINUX_H_ |