diff options
26 files changed, 833 insertions, 598 deletions
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index bf34323..aa2a150 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -1629,6 +1629,11 @@ int ChromeBrowserMainParts::PreMainMessageLoopRunImpl() { BrowserThread::IO, FROM_HERE, base::Bind(nacl::NaClProcessHost::EarlyStartup)); + +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, + base::Bind(nacl::NaClProcessHost::EarlyZygoteLaunch)); +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) #endif // !defined(DISABLE_NACL) // Make sure initial prefs are recorded diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc index b53eb2d..ea551c7 100644 --- a/chrome/browser/memory_details.cc +++ b/chrome/browser/memory_details.cc @@ -211,11 +211,6 @@ void MemoryDetails::CollectChildInfoOnIOThread(CollectionMode mode) { void MemoryDetails::CollectChildInfoOnUIThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) - const pid_t zygote_pid = content::ZygoteHost::GetInstance()->GetPid(); -#endif - ProcessData* const chrome_browser = ChromeBrowser(); // First pass, collate the widgets by process ID. @@ -367,7 +362,7 @@ void MemoryDetails::CollectChildInfoOnUIThread() { } #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) - if (process.pid == zygote_pid) { + if (content::ZygoteHost::GetInstance()->IsZygotePid(process.pid)) { process.process_type = content::PROCESS_TYPE_ZYGOTE; } #endif diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc index 1fdbd52..41c58cd 100644 --- a/chrome/browser/ui/webui/about_ui.cc +++ b/chrome/browser/ui/webui/about_ui.cc @@ -746,7 +746,8 @@ std::string AboutSandbox() { data.append("</h1>"); // Get expected sandboxing status of renderers. - const int status = content::ZygoteHost::GetInstance()->GetSandboxStatus(); + const int status = + content::ZygoteHost::GetInstance()->GetRendererSandboxStatus(); data.append("<table>"); diff --git a/components/nacl/browser/nacl_process_host.cc b/components/nacl/browser/nacl_process_host.cc index aa4f4c5..3b57e14 100644 --- a/components/nacl/browser/nacl_process_host.cc +++ b/components/nacl/browser/nacl_process_host.cc @@ -67,6 +67,7 @@ #include <netinet/in.h> #include <sys/socket.h> +#include "content/public/browser/zygote_handle_linux.h" #include "ipc/ipc_channel_posix.h" #elif defined(OS_WIN) #include <windows.h> @@ -154,6 +155,10 @@ bool RunningOnWOW64() { namespace { +#if defined(OS_POSIX) && !defined(OS_MACOSX) +content::ZygoteHandle g_nacl_zygote; +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + // NOTE: changes to this class need to be reviewed by the security team. class NaClSandboxedProcessLauncherDelegate : public content::SandboxedProcessLauncherDelegate { @@ -180,7 +185,10 @@ class NaClSandboxedProcessLauncherDelegate } } #elif defined(OS_POSIX) - bool ShouldUseZygote() override { return true; } +#if !defined(OS_MACOSX) + content::ZygoteHandle* GetZygote() override { return &g_nacl_zygote; } +#endif // !defined(OS_MACOSX) + base::ScopedFD TakeIpcFd() override { return std::move(ipc_fd_); } #endif // OS_WIN @@ -401,6 +409,14 @@ void NaClProcessHost::EarlyStartup() { NaClBrowser::GetDelegate()->SetDebugPatterns(nacl_debug_mask); } +#if defined(OS_POSIX) && !defined(OS_MACOSX) +// static +void NaClProcessHost::EarlyZygoteLaunch() { + DCHECK(!g_nacl_zygote); + g_nacl_zygote = content::CreateZygote(); +} +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + // static void NaClProcessHost::SetPpapiKeepAliveThrottleForTesting( unsigned milliseconds) { diff --git a/components/nacl/browser/nacl_process_host.h b/components/nacl/browser/nacl_process_host.h index aec4918..0178350 100644 --- a/components/nacl/browser/nacl_process_host.h +++ b/components/nacl/browser/nacl_process_host.h @@ -93,6 +93,11 @@ class NaClProcessHost : public content::BrowserChildProcessHostDelegate { // Do any minimal work that must be done at browser startup. static void EarlyStartup(); +#if defined(OS_POSIX) && !defined(OS_MACOSX) + // Launch the NaCl zygote early in the browser startup. + static void EarlyZygoteLaunch(); +#endif // defined(OS_POSIX) && !defined(OS_MACOSX) + // Specifies throttling time in milliseconds for PpapiHostMsg_Keepalive IPCs. static void SetPpapiKeepAliveThrottleForTesting(unsigned milliseconds); diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index 6bc1ea90..98a6ddf 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc @@ -53,9 +53,11 @@ #include "content/browser/mojo/mojo_shell_context.h" #include "content/browser/net/browser_online_state_observer.h" #include "content/browser/renderer_host/media/media_stream_manager.h" +#include "content/browser/renderer_host/render_process_host_impl.h" #include "content/browser/speech/speech_recognition_manager_impl.h" #include "content/browser/startup_task_runner.h" #include "content/browser/time_zone_monitor.h" +#include "content/browser/utility_process_host_impl.h" #include "content/browser/webui/content_web_ui_controller_factory.h" #include "content/browser/webui/url_data_manager.h" #include "content/common/content_switches_internal.h" @@ -152,6 +154,10 @@ #include "content/browser/renderer_host/render_sandbox_host_linux.h" #include "content/browser/zygote_host/zygote_host_impl_linux.h" #include "sandbox/linux/suid/client/setuid_sandbox_host.h" + +#if !defined(OS_ANDROID) +#include "content/browser/ppapi_plugin_process_host.h" +#endif #endif #if defined(ENABLE_PLUGINS) @@ -220,6 +226,9 @@ void SetupSandbox(const base::CommandLine& parsed_command_line) { // Tickle the sandbox host and zygote host so they fork now. RenderSandboxHostLinux::GetInstance()->Init(); + RenderProcessHostImpl::EarlyZygoteLaunch(); + PpapiPluginProcessHost::EarlyZygoteLaunch(); + UtilityProcessHostImpl::EarlyZygoteLaunch(); ZygoteHostImpl::GetInstance()->Init(sandbox_binary.value()); } #endif diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc index 57a6368..4398e2b 100644 --- a/content/browser/child_process_launcher.cc +++ b/content/browser/child_process_launcher.cc @@ -39,8 +39,10 @@ #elif defined(OS_POSIX) #include "base/memory/singleton.h" #include "content/browser/renderer_host/render_sandbox_host_linux.h" +#include "content/browser/zygote_host/zygote_communication_linux.h" #include "content/browser/zygote_host/zygote_host_impl_linux.h" #include "content/common/child_process_sandbox_support_impl_linux.h" +#include "content/public/browser/zygote_handle_linux.h" #endif #if defined(OS_POSIX) @@ -53,7 +55,7 @@ namespace content { namespace { -typedef base::Callback<void(bool, +typedef base::Callback<void(ZygoteHandle, #if defined(OS_ANDROID) base::ScopedFD, #endif @@ -87,7 +89,7 @@ void OnChildProcessStartedAndroid(const NotifyCallback& callback, base::Bind(&RecordHistogramsOnLauncherThread, launch_time)); base::Closure callback_on_client_thread( - base::Bind(callback, false, base::Passed(&ipcfd), + base::Bind(callback, nullptr, base::Passed(&ipcfd), base::Passed(base::Process(handle)))); if (BrowserThread::CurrentlyOn(client_thread_id)) { callback_on_client_thread.Run(); @@ -108,15 +110,15 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, base::CommandLine* cmd_line) { DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); scoped_ptr<SandboxedProcessLauncherDelegate> delegate_deleter(delegate); +#if !defined(OS_ANDROID) + ZygoteHandle zygote = nullptr; +#endif #if defined(OS_WIN) - bool use_zygote = false; bool launch_elevated = delegate->ShouldLaunchElevated(); #elif defined(OS_MACOSX) - bool use_zygote = false; base::EnvironmentMap env = delegate->GetEnvironment(); base::ScopedFD ipcfd = delegate->TakeIpcFd(); #elif defined(OS_POSIX) && !defined(OS_ANDROID) - bool use_zygote = delegate->ShouldUseZygote(); base::EnvironmentMap env = delegate->GetEnvironment(); base::ScopedFD ipcfd = delegate->TakeIpcFd(); #endif @@ -199,8 +201,16 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, // child termination. #if !defined(OS_MACOSX) - if (use_zygote) { - base::ProcessHandle handle = ZygoteHostImpl::GetInstance()->ForkRequest( + ZygoteHandle* zygote_handle = delegate->GetZygote(); + // If |zygote_handle| is null, a zygote should not be used. + if (zygote_handle) { + // This code runs on the PROCESS_LAUNCHER thread so race conditions are not + // an issue with the lazy initialization. + if (*zygote_handle == nullptr) { + *zygote_handle = CreateZygote(); + } + zygote = *zygote_handle; + base::ProcessHandle handle = zygote->ForkRequest( cmd_line->argv(), std::move(files_to_register), process_type); process = base::Process(handle); } else @@ -278,13 +288,11 @@ void LaunchOnLauncherThread(const NotifyCallback& callback, begin_launch_time); } BrowserThread::PostTask(client_thread_id, FROM_HERE, - base::Bind(callback, - use_zygote, - base::Passed(&process))); + base::Bind(callback, zygote, base::Passed(&process))); #endif // !defined(OS_ANDROID) } -void TerminateOnLauncherThread(bool zygote, base::Process process) { +void TerminateOnLauncherThread(ZygoteHandle zygote, base::Process process) { DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); #if defined(OS_ANDROID) VLOG(1) << "ChromeProcess: Stopping process with handle " @@ -300,7 +308,7 @@ void TerminateOnLauncherThread(bool zygote, base::Process process) { if (zygote) { // If the renderer was created via a zygote, we have to proxy the reaping // through the zygote process. - ZygoteHostImpl::GetInstance()->EnsureProcessTerminated(process.Handle()); + zygote->EnsureProcessTerminated(process.Handle()); } else #endif // !OS_MACOSX base::EnsureProcessTerminated(std::move(process)); @@ -330,7 +338,7 @@ ChildProcessLauncher::ChildProcessLauncher( : client_(client), termination_status_(base::TERMINATION_STATUS_NORMAL_TERMINATION), exit_code_(RESULT_CODE_NORMAL_EXIT), - zygote_(false), + zygote_(nullptr), starting_(true), #if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ @@ -400,8 +408,8 @@ void ChildProcessLauncher::UpdateTerminationStatus(bool known_dead) { DCHECK(CalledOnValidThread()); #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) if (zygote_) { - termination_status_ = ZygoteHostImpl::GetInstance()-> - GetTerminationStatus(process_.Handle(), known_dead, &exit_code_); + termination_status_ = zygote_->GetTerminationStatus( + process_.Handle(), known_dead, &exit_code_); } else if (known_dead) { termination_status_ = base::GetKnownDeadTerminationStatus(process_.Handle(), &exit_code_); @@ -434,7 +442,7 @@ void ChildProcessLauncher::SetProcessBackgrounded(bool background) { void ChildProcessLauncher::DidLaunch( base::WeakPtr<ChildProcessLauncher> instance, bool terminate_on_shutdown, - bool zygote, + ZygoteHandle zygote, #if defined(OS_ANDROID) base::ScopedFD ipcfd, #endif @@ -459,12 +467,11 @@ void ChildProcessLauncher::DidLaunch( } } -void ChildProcessLauncher::Notify( - bool zygote, +void ChildProcessLauncher::Notify(ZygoteHandle zygote, #if defined(OS_ANDROID) - base::ScopedFD ipcfd, + base::ScopedFD ipcfd, #endif - base::Process process) { + base::Process process) { DCHECK(CalledOnValidThread()); starting_ = false; process_ = std::move(process); diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h index c4f6321..9233f43 100644 --- a/content/browser/child_process_launcher.h +++ b/content/browser/child_process_launcher.h @@ -15,13 +15,13 @@ #include "build/build_config.h" #include "content/common/content_export.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/sandboxed_process_launcher_delegate.h" namespace base { class CommandLine; } namespace content { -class SandboxedProcessLauncherDelegate; // Launches a process asynchronously and notifies the client of the process // handle when it's available. It's used to avoid blocking the calling thread @@ -95,14 +95,14 @@ class CONTENT_EXPORT ChildProcessLauncher : public base::NonThreadSafe { // client went away. static void DidLaunch(base::WeakPtr<ChildProcessLauncher> instance, bool terminate_on_shutdown, - bool zygote, + ZygoteHandle zygote, #if defined(OS_ANDROID) base::ScopedFD ipcfd, #endif base::Process process); // Notifies the client about the result of the operation. - void Notify(bool zygote, + void Notify(ZygoteHandle zygote, #if defined(OS_ANDROID) base::ScopedFD ipcfd, #endif @@ -122,7 +122,7 @@ class CONTENT_EXPORT ChildProcessLauncher : public base::NonThreadSafe { base::Process process_; base::TerminationStatus termination_status_; int exit_code_; - bool zygote_; + ZygoteHandle zygote_; bool starting_; // Controls whether the child process should be terminated on browser // shutdown. Default behavior is to terminate the child. diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc index 74c6e0f..766a713 100644 --- a/content/browser/ppapi_plugin_process_host.cc +++ b/content/browser/ppapi_plugin_process_host.cc @@ -35,6 +35,10 @@ #include "ppapi/proxy/ppapi_messages.h" #include "ui/base/ui_base_switches.h" +#if defined(OS_POSIX) +#include "content/public/browser/zygote_handle_linux.h" +#endif // defined(OS_POSIX) + #if defined(OS_WIN) #include "content/browser/renderer_host/dwrite_font_proxy_message_filter_win.h" #include "content/common/sandbox_win.h" @@ -45,6 +49,10 @@ namespace content { +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +ZygoteHandle g_ppapi_zygote; +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // NOTE: changes to this class need to be reviewed by the security team. class PpapiPluginSandboxedProcessLauncherDelegate : public content::SandboxedProcessLauncherDelegate { @@ -100,13 +108,18 @@ class PpapiPluginSandboxedProcessLauncherDelegate } #elif defined(OS_POSIX) - bool ShouldUseZygote() override { +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) + ZygoteHandle* GetZygote() override { const base::CommandLine& browser_command_line = *base::CommandLine::ForCurrentProcess(); base::CommandLine::StringType plugin_launcher = browser_command_line .GetSwitchValueNative(switches::kPpapiPluginLauncher); - return !is_broker_ && plugin_launcher.empty(); + if (is_broker_ || !plugin_launcher.empty()) + return nullptr; + return &g_ppapi_zygote; } +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) + base::ScopedFD TakeIpcFd() override { return std::move(ipc_fd_); } #endif // OS_WIN @@ -195,6 +208,14 @@ PpapiPluginProcessHost* PpapiPluginProcessHost::CreateBrokerHost( return NULL; } +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +// static +void PpapiPluginProcessHost::EarlyZygoteLaunch() { + DCHECK(!g_ppapi_zygote); + g_ppapi_zygote = CreateZygote(); +} +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // static void PpapiPluginProcessHost::DidCreateOutOfProcessInstance( int plugin_process_id, diff --git a/content/browser/ppapi_plugin_process_host.h b/content/browser/ppapi_plugin_process_host.h index 9659349..85e2b64d 100644 --- a/content/browser/ppapi_plugin_process_host.h +++ b/content/browser/ppapi_plugin_process_host.h @@ -116,6 +116,11 @@ class PpapiPluginProcessHost : public BrowserChildProcessHostDelegate, return profile_data_directory_; } +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // Launch the zygote early in the browser startup. + static void EarlyZygoteLaunch(); +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // The client pointer must remain valid until its callback is issued. private: diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 67f45e6..466f5a5 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -202,6 +202,12 @@ #include "content/browser/mach_broker_mac.h" #endif +#if defined(OS_POSIX) +#include "content/browser/zygote_host/zygote_communication_linux.h" +#include "content/browser/zygote_host/zygote_host_impl_linux.h" +#include "content/public/browser/zygote_handle_linux.h" +#endif // defined(OS_POSIX) + #if defined(USE_OZONE) #include "ui/ozone/public/client_native_pixmap_factory.h" #include "ui/ozone/public/ozone_platform.h" @@ -363,6 +369,12 @@ SiteProcessMap* GetSiteProcessMapForBrowserContext(BrowserContext* context) { return map; } +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +// This static member variable holds the zygote communication information for +// the renderer. +ZygoteHandle g_render_zygote; +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // NOTE: changes to this class need to be reviewed by the security team. class RendererSandboxedProcessLauncherDelegate : public SandboxedProcessLauncherDelegate { @@ -390,13 +402,17 @@ class RendererSandboxedProcessLauncherDelegate } #elif defined(OS_POSIX) - bool ShouldUseZygote() override { +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) + ZygoteHandle* GetZygote() override { const base::CommandLine& browser_command_line = *base::CommandLine::ForCurrentProcess(); base::CommandLine::StringType renderer_prefix = browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); - return renderer_prefix.empty(); + if (!renderer_prefix.empty()) + return nullptr; + return &g_render_zygote; } +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) base::ScopedFD TakeIpcFd() override { return std::move(ipc_fd_); } #endif // OS_WIN @@ -526,6 +542,19 @@ void RenderProcessHost::SetMaxRendererProcessCount(size_t count) { g_max_renderer_count_override = count; } +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +// static +void RenderProcessHostImpl::EarlyZygoteLaunch() { + DCHECK(!g_render_zygote); + g_render_zygote = CreateZygote(); + // TODO(kerrnel): Investigate doing this without the ZygoteHostImpl as a + // proxy. It is currently done this way due to concerns about race + // conditions. + ZygoteHostImpl::GetInstance()->SetRendererSandboxStatus( + g_render_zygote->GetSandboxStatus()); +} +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + RenderProcessHostImpl::RenderProcessHostImpl( BrowserContext* browser_context, StoragePartitionImpl* storage_partition_impl, diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 7915495..4f59650 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -263,6 +263,11 @@ class CONTENT_EXPORT RenderProcessHostImpl BluetoothDispatcherHost* GetBluetoothDispatcherHost(); +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // Launch the zygote early in the browser startup. + static void EarlyZygoteLaunch(); +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + protected: // A proxy for our IPC::Channel that lives on the IO thread. scoped_ptr<IPC::ChannelProxy> channel_; diff --git a/content/browser/utility_process_host_impl.cc b/content/browser/utility_process_host_impl.cc index db67d60..447685a 100644 --- a/content/browser/utility_process_host_impl.cc +++ b/content/browser/utility_process_host_impl.cc @@ -37,6 +37,10 @@ #include "ipc/ipc_switches.h" #include "ui/base/ui_base_switches.h" +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +#include "content/public/browser/zygote_handle_linux.h" +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + #if defined(OS_WIN) #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/sandbox_types.h" @@ -44,6 +48,12 @@ namespace content { +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +namespace { +ZygoteHandle g_utility_zygote; +} // namespace +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // NOTE: changes to this class need to be reviewed by the security team. class UtilitySandboxedProcessLauncherDelegate : public SandboxedProcessLauncherDelegate { @@ -58,7 +68,9 @@ class UtilitySandboxedProcessLauncherDelegate launch_elevated_(launch_elevated) #elif defined(OS_POSIX) env_(env), +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) no_sandbox_(no_sandbox), +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) ipc_fd_(host->TakeClientFileDescriptor()) #endif // OS_WIN {} @@ -88,9 +100,13 @@ class UtilitySandboxedProcessLauncherDelegate #elif defined(OS_POSIX) - bool ShouldUseZygote() override { - return !no_sandbox_ && exposed_dir_.empty(); +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) + ZygoteHandle* GetZygote() override { + if (no_sandbox_ || !exposed_dir_.empty()) + return nullptr; + return &g_utility_zygote; } +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) base::EnvironmentMap GetEnvironment() override { return env_; } base::ScopedFD TakeIpcFd() override { return std::move(ipc_fd_); } #endif // OS_WIN @@ -106,7 +122,9 @@ class UtilitySandboxedProcessLauncherDelegate bool launch_elevated_; #elif defined(OS_POSIX) base::EnvironmentMap env_; +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) bool no_sandbox_; +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) base::ScopedFD ipc_fd_; #endif // OS_WIN }; @@ -220,6 +238,14 @@ void UtilityProcessHostImpl::SetName(const base::string16& name) { name_ = name; } +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +// static +void UtilityProcessHostImpl::EarlyZygoteLaunch() { + DCHECK(!g_utility_zygote); + g_utility_zygote = CreateZygote(); +} +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + bool UtilityProcessHostImpl::StartProcess() { if (started_) return true; diff --git a/content/browser/utility_process_host_impl.h b/content/browser/utility_process_host_impl.h index 94aafa7..46dc6bf 100644 --- a/content/browser/utility_process_host_impl.h +++ b/content/browser/utility_process_host_impl.h @@ -63,6 +63,11 @@ class CONTENT_EXPORT UtilityProcessHostImpl void set_child_flags(int flags) { child_flags_ = flags; } +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + // Launch the zygote early in the browser startup. + static void EarlyZygoteLaunch(); +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) + private: // Starts a process if necessary. Returns true if it succeeded or a process // has already been started via StartBatchMode(). diff --git a/content/browser/zygote_host/zygote_communication_linux.cc b/content/browser/zygote_host/zygote_communication_linux.cc new file mode 100644 index 0000000..751f3b3 --- /dev/null +++ b/content/browser/zygote_host/zygote_communication_linux.cc @@ -0,0 +1,459 @@ +// Copyright 2015 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 "content/browser/zygote_host/zygote_communication_linux.h" + +#include <string.h> +#include <sys/socket.h> + +#include "base/base_switches.h" +#include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/metrics/sparse_histogram.h" +#include "base/path_service.h" +#include "base/pickle.h" +#include "base/posix/eintr_wrapper.h" +#include "base/posix/unix_domain_socket_linux.h" +#include "content/browser/renderer_host/render_sandbox_host_linux.h" +#include "content/browser/zygote_host/zygote_host_impl_linux.h" +#include "content/common/child_process_sandbox_support_impl_linux.h" +#include "content/common/zygote_commands_linux.h" +#include "content/public/browser/content_browser_client.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/result_codes.h" +#include "sandbox/linux/services/credentials.h" +#include "sandbox/linux/services/namespace_sandbox.h" +#include "sandbox/linux/suid/client/setuid_sandbox_host.h" +#include "ui/gfx/switches.h" + +namespace content { + +namespace { + +// Receive a fixed message on fd and return the sender's PID. +// Returns true if the message received matches the expected message. +bool ReceiveFixedMessage(int fd, + const char* expect_msg, + size_t expect_len, + base::ProcessId* sender_pid) { + char buf[expect_len + 1]; + std::vector<base::ScopedFD> fds_vec; + + const ssize_t len = base::UnixDomainSocket::RecvMsgWithPid( + fd, buf, sizeof(buf), &fds_vec, sender_pid); + if (static_cast<size_t>(len) != expect_len) + return false; + if (memcmp(buf, expect_msg, expect_len) != 0) + return false; + if (!fds_vec.empty()) + return false; + return true; +} + +} // namespace + +ZygoteCommunication::ZygoteCommunication() + : control_fd_(-1), + control_lock_(), + pid_(), + list_of_running_zygote_children_(), + child_tracking_lock_(), + sandbox_status_(0), + have_read_sandbox_status_word_(false), + use_suid_sandbox_for_adj_oom_score_(false), + init_(false) {} + +ZygoteCommunication::~ZygoteCommunication() {} + +bool ZygoteCommunication::SendMessage(const base::Pickle& data, + const std::vector<int>* fds) { + DCHECK_NE(-1, control_fd_); + CHECK(data.size() <= kZygoteMaxMessageLength) + << "Trying to send too-large message to zygote (sending " << data.size() + << " bytes, max is " << kZygoteMaxMessageLength << ")"; + CHECK(!fds || fds->size() <= base::UnixDomainSocket::kMaxFileDescriptors) + << "Trying to send message with too many file descriptors to zygote " + << "(sending " << fds->size() << ", max is " + << base::UnixDomainSocket::kMaxFileDescriptors << ")"; + + return base::UnixDomainSocket::SendMsg(control_fd_, data.data(), data.size(), + fds ? *fds : std::vector<int>()); +} + +ssize_t ZygoteCommunication::ReadSandboxStatus() { + DCHECK_NE(-1, control_fd_); + // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, + // but don't wait for the reply. Thus, the first time that we read from the + // zygote, we get the reply to that request. + ssize_t bytes_read = HANDLE_EINTR( + read(control_fd_, &sandbox_status_, sizeof(sandbox_status_))); + if (bytes_read != sizeof(sandbox_status_)) { + return -1; + } + return bytes_read; +} + +ssize_t ZygoteCommunication::ReadReply(void* buf, size_t buf_len) { + DCHECK_NE(-1, control_fd_); + if (!have_read_sandbox_status_word_) { + if (ReadSandboxStatus() == -1) { + return -1; + } + have_read_sandbox_status_word_ = true; + UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_); + } + + return HANDLE_EINTR(read(control_fd_, buf, buf_len)); +} + +pid_t ZygoteCommunication::ForkRequest(const std::vector<std::string>& argv, + scoped_ptr<FileDescriptorInfo> mapping, + const std::string& process_type) { + DCHECK(init_); + + base::Pickle pickle; + int raw_socks[2]; + PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks)); + base::ScopedFD my_sock(raw_socks[0]); + base::ScopedFD peer_sock(raw_socks[1]); + CHECK(base::UnixDomainSocket::EnableReceiveProcessId(my_sock.get())); + + pickle.WriteInt(kZygoteCommandFork); + pickle.WriteString(process_type); + pickle.WriteInt(argv.size()); + for (std::vector<std::string>::const_iterator i = argv.begin(); + i != argv.end(); ++i) + pickle.WriteString(*i); + + // Fork requests contain one file descriptor for the PID oracle, and one + // more for each file descriptor mapping for the child process. + const size_t num_fds_to_send = 1 + mapping->GetMappingSize(); + pickle.WriteInt(num_fds_to_send); + + std::vector<int> fds; + + // First FD to send is peer_sock. + // TODO(morrita): Ideally, this should be part of the mapping so that + // FileDescriptorInfo can manages its lifetime. + fds.push_back(peer_sock.get()); + + // The rest come from mapping. + for (size_t i = 0; i < mapping->GetMappingSize(); ++i) { + pickle.WriteUInt32(mapping->GetIDAt(i)); + fds.push_back(mapping->GetFDAt(i)); + } + + // Sanity check that we've populated |fds| correctly. + DCHECK_EQ(num_fds_to_send, fds.size()); + + pid_t pid; + { + base::AutoLock lock(control_lock_); + if (!SendMessage(pickle, &fds)) + return base::kNullProcessHandle; + mapping.reset(); + peer_sock.reset(); + + { + char buf[sizeof(kZygoteChildPingMessage) + 1]; + std::vector<base::ScopedFD> recv_fds; + base::ProcessId real_pid; + + ssize_t n = base::UnixDomainSocket::RecvMsgWithPid( + my_sock.get(), buf, sizeof(buf), &recv_fds, &real_pid); + if (n != sizeof(kZygoteChildPingMessage) || + 0 != memcmp(buf, kZygoteChildPingMessage, + sizeof(kZygoteChildPingMessage))) { + // Zygote children should still be trustworthy when they're supposed to + // ping us, so something's broken if we don't receive a valid ping. + LOG(ERROR) << "Did not receive ping from zygote child"; + NOTREACHED(); + real_pid = -1; + } + my_sock.reset(); + + // Always send PID back to zygote. + base::Pickle pid_pickle; + pid_pickle.WriteInt(kZygoteCommandForkRealPID); + pid_pickle.WriteInt(real_pid); + if (!SendMessage(pid_pickle, NULL)) + return base::kNullProcessHandle; + } + + // Read the reply, which pickles the PID and an optional UMA enumeration. + static const unsigned kMaxReplyLength = 2048; + char buf[kMaxReplyLength]; + const ssize_t len = ReadReply(buf, sizeof(buf)); + + base::Pickle reply_pickle(buf, len); + base::PickleIterator iter(reply_pickle); + if (len <= 0 || !iter.ReadInt(&pid)) + return base::kNullProcessHandle; + + // If there is a nonempty UMA name string, then there is a UMA + // enumeration to record. + std::string uma_name; + int uma_sample; + int uma_boundary_value; + if (iter.ReadString(&uma_name) && !uma_name.empty() && + iter.ReadInt(&uma_sample) && iter.ReadInt(&uma_boundary_value)) { + // We cannot use the UMA_HISTOGRAM_ENUMERATION macro here, + // because that's only for when the name is the same every time. + // Here we're using whatever name we got from the other side. + // But since it's likely that the same one will be used repeatedly + // (even though it's not guaranteed), we cache it here. + static base::HistogramBase* uma_histogram; + if (!uma_histogram || uma_histogram->histogram_name() != uma_name) { + uma_histogram = base::LinearHistogram::FactoryGet( + uma_name, 1, uma_boundary_value, uma_boundary_value + 1, + base::HistogramBase::kUmaTargetedHistogramFlag); + } + uma_histogram->Add(uma_sample); + } + + if (pid <= 0) + return base::kNullProcessHandle; + } + +#if !defined(OS_OPENBSD) + // This is just a starting score for a renderer or extension (the + // only types of processes that will be started this way). It will + // get adjusted as time goes on. (This is the same value as + // chrome::kLowestRendererOomScore in chrome/chrome_constants.h, but + // that's not something we can include here.) + const int kLowestRendererOomScore = 300; + ZygoteHostImpl::GetInstance()->AdjustRendererOOMScore( + pid, kLowestRendererOomScore); +#endif + + ZygoteChildBorn(pid); + return pid; +} + +void ZygoteCommunication::EnsureProcessTerminated(pid_t process) { + DCHECK(init_); + base::Pickle pickle; + + pickle.WriteInt(kZygoteCommandReap); + pickle.WriteInt(process); + if (!SendMessage(pickle, NULL)) + LOG(ERROR) << "Failed to send Reap message to zygote"; + ZygoteChildDied(process); +} + +void ZygoteCommunication::ZygoteChildBorn(pid_t process) { + base::AutoLock lock(child_tracking_lock_); + bool new_element_inserted = + list_of_running_zygote_children_.insert(process).second; + DCHECK(new_element_inserted); +} + +void ZygoteCommunication::ZygoteChildDied(pid_t process) { + base::AutoLock lock(child_tracking_lock_); + size_t num_erased = list_of_running_zygote_children_.erase(process); + DCHECK_EQ(1U, num_erased); +} + +void ZygoteCommunication::Init() { + CHECK(!init_); + + base::FilePath chrome_path; + CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); + base::CommandLine cmd_line(chrome_path); + + cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); + + int fds[2]; + CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); + CHECK(base::UnixDomainSocket::EnableReceiveProcessId(fds[0])); + base::FileHandleMappingVector fds_to_map; + fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); + + base::LaunchOptions options; + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { + cmd_line.PrependWrapper( + browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); + } + // Append any switches from the browser process that need to be forwarded on + // to the zygote/renderers. + // Should this list be obtained from browser_render_process_host.cc? + static const char* kForwardSwitches[] = { + switches::kAllowSandboxDebugging, switches::kDisableSeccompFilterSandbox, + switches::kEnableHeapProfiling, + switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. + // Zygote process needs to know what resources to have loaded when it + // becomes a renderer process. + switches::kForceDeviceScaleFactor, switches::kLoggingLevel, + switches::kNoSandbox, switches::kPpapiInProcess, + switches::kRegisterPepperPlugins, switches::kV, switches::kVModule, + }; + cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, + arraysize(kForwardSwitches)); + + GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1); + + const bool using_namespace_sandbox = ShouldUseNamespaceSandbox(); + // A non empty sandbox_cmd means we want a SUID sandbox. + const bool using_suid_sandbox = + !ZygoteHostImpl::GetInstance()->SandboxCommand().empty() && + !using_namespace_sandbox; + // Use the SUID sandbox for adjusting OOM scores when we are using the setuid + // or namespace sandbox. This is needed beacuse the processes are + // non-dumpable, so /proc/pid/oom_score_adj can only be written by root. + use_suid_sandbox_for_adj_oom_score_ = + !ZygoteHostImpl::GetInstance()->SandboxCommand().empty() && + using_suid_sandbox; + + // Start up the sandbox host process and get the file descriptor for the + // renderers to talk to it. + const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); + fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD())); + + base::ScopedFD dummy_fd; + if (using_suid_sandbox) { + scoped_ptr<sandbox::SetuidSandboxHost> sandbox_host( + sandbox::SetuidSandboxHost::Create()); + sandbox_host->PrependWrapper(&cmd_line); + sandbox_host->SetupLaunchOptions(&options, &fds_to_map, &dummy_fd); + sandbox_host->SetupLaunchEnvironment(); + } + + options.fds_to_remap = &fds_to_map; + base::Process process = + using_namespace_sandbox + ? sandbox::NamespaceSandbox::LaunchProcess(cmd_line, options) + : base::LaunchProcess(cmd_line, options); + CHECK(process.IsValid()) << "Failed to launch zygote process"; + + dummy_fd.reset(); + + if (using_suid_sandbox || using_namespace_sandbox) { + // The SUID sandbox will execute the zygote in a new PID namespace, and + // the main zygote process will then fork from there. Watch now our + // elaborate dance to find and validate the zygote's PID. + + // First we receive a message from the zygote boot process. + base::ProcessId boot_pid; + CHECK(ReceiveFixedMessage(fds[0], kZygoteBootMessage, + sizeof(kZygoteBootMessage), &boot_pid)); + + // Within the PID namespace, the zygote boot process thinks it's PID 1, + // but its real PID can never be 1. This gives us a reliable test that + // the kernel is translating the sender's PID to our namespace. + CHECK_GT(boot_pid, 1) + << "Received invalid process ID for zygote; kernel might be too old? " + "See crbug.com/357670 or try using --" + << switches::kDisableSetuidSandbox << " to workaround."; + + // Now receive the message that the zygote's ready to go, along with the + // main zygote process's ID. + CHECK(ReceiveFixedMessage(fds[0], kZygoteHelloMessage, + sizeof(kZygoteHelloMessage), &pid_)); + CHECK_GT(pid_, 1); + + if (process.Pid() != pid_) { + // Reap the sandbox. + base::EnsureProcessGetsReaped(process.Pid()); + } + } else { + // Not using the SUID sandbox. + // Note that ~base::Process() will reset the internal value, but there's no + // real "handle" on POSIX so that is safe. + pid_ = process.Pid(); + } + + close(fds[1]); + control_fd_ = fds[0]; + + ZygoteHostImpl::GetInstance()->AddZygotePid(pid_); + + base::Pickle pickle; + pickle.WriteInt(kZygoteCommandGetSandboxStatus); + if (!SendMessage(pickle, NULL)) + LOG(FATAL) << "Cannot communicate with zygote"; + + init_ = true; +} + +base::TerminationStatus ZygoteCommunication::GetTerminationStatus( + base::ProcessHandle handle, + bool known_dead, + int* exit_code) { + DCHECK(init_); + base::Pickle pickle; + pickle.WriteInt(kZygoteCommandGetTerminationStatus); + pickle.WriteBool(known_dead); + pickle.WriteInt(handle); + + static const unsigned kMaxMessageLength = 128; + char buf[kMaxMessageLength]; + ssize_t len; + { + base::AutoLock lock(control_lock_); + if (!SendMessage(pickle, NULL)) + LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote"; + len = ReadReply(buf, sizeof(buf)); + } + + // Set this now to handle the error cases. + if (exit_code) + *exit_code = RESULT_CODE_NORMAL_EXIT; + int status = base::TERMINATION_STATUS_NORMAL_TERMINATION; + + if (len == -1) { + LOG(WARNING) << "Error reading message from zygote: " << errno; + } else if (len == 0) { + LOG(WARNING) << "Socket closed prematurely."; + } else { + base::Pickle read_pickle(buf, len); + int tmp_status, tmp_exit_code; + base::PickleIterator iter(read_pickle); + if (!iter.ReadInt(&tmp_status) || !iter.ReadInt(&tmp_exit_code)) { + LOG(WARNING) + << "Error parsing GetTerminationStatus response from zygote."; + } else { + if (exit_code) + *exit_code = tmp_exit_code; + status = tmp_status; + } + } + + if (status != base::TERMINATION_STATUS_STILL_RUNNING) { + ZygoteChildDied(handle); + } + return static_cast<base::TerminationStatus>(status); +} + +bool ZygoteCommunication::ShouldUseNamespaceSandbox() { + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + if (command_line.HasSwitch(switches::kNoSandbox)) { + return false; + } + + if (command_line.HasSwitch(switches::kDisableNamespaceSandbox)) { + return false; + } + + if (!sandbox::Credentials::CanCreateProcessInNewUserNS()) { + return false; + } + + return true; +} + +int ZygoteCommunication::GetSandboxStatus() { + if (have_read_sandbox_status_word_) { + return sandbox_status_; + } + if (ReadSandboxStatus() == -1) { + return 0; + } + have_read_sandbox_status_word_ = true; + return sandbox_status_; +} + +} // namespace content diff --git a/content/browser/zygote_host/zygote_communication_linux.h b/content/browser/zygote_host/zygote_communication_linux.h new file mode 100644 index 0000000..074a4f4 --- /dev/null +++ b/content/browser/zygote_host/zygote_communication_linux.h @@ -0,0 +1,92 @@ +// Copyright 2015 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_COMMUNICATION_H_ +#define CONTENT_COMMON_ZYGOTE_COMMUNICATION_H_ + +#include <set> +#include <vector> + +#include "base/process/kill.h" +#include "base/synchronization/lock.h" +#include "content/common/content_export.h" +#include "content/public/browser/file_descriptor_info.h" + +namespace content { + +class CONTENT_EXPORT ZygoteCommunication { + public: + ZygoteCommunication(); + ~ZygoteCommunication(); + + void Init(); + + // Tries to start a process of type indicated by process_type. + // Returns its pid on success, otherwise base::kNullProcessHandle; + pid_t ForkRequest(const std::vector<std::string>& command_line, + scoped_ptr<FileDescriptorInfo> mapping, + const std::string& process_type); + + void EnsureProcessTerminated(pid_t process); + + // Should be called every time a Zygote child died. + void ZygoteChildDied(pid_t process); + + // Get the termination status (and, optionally, the exit code) of + // the process. |exit_code| is set to the exit code of the child + // process. (|exit_code| may be NULL.) + // Unfortunately the Zygote can not accurately figure out if a process + // is already dead without waiting synchronously for it. + // |known_dead| should be set to true when we already know that the process + // is dead. When |known_dead| is false, processes could be seen as + // still running, even when they're not. When |known_dead| is true, the + // process will be SIGKILL-ed first (which should have no effect if it was + // really dead). This is to prevent a waiting waitpid() from blocking in + // a single-threaded Zygote. See crbug.com/157458. + base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle, + bool known_dead, + int* exit_code); + + // Returns the sandbox status of this zygote. + int GetSandboxStatus(); + + private: + // Whether we should use the namespace sandbox instead of the setuid sandbox. + bool ShouldUseNamespaceSandbox(); + + // Should be called every time a Zygote child is born. + void ZygoteChildBorn(pid_t process); + + // Read the reply from the zygote. + ssize_t ReadReply(void* buf, size_t buf_len); + + // Sends |data| to the zygote via |control_fd_|. If |fds| is non-NULL, the + // included file descriptors will also be passed. The caller is responsible + // for acquiring |control_lock_|. + bool SendMessage(const base::Pickle& data, const std::vector<int>* fds); + + // Get the sandbox status from the zygote. + ssize_t ReadSandboxStatus(); + + int control_fd_; // the socket to the zygote. + // A lock protecting all communication with the zygote. This lock must be + // acquired before sending a command and released after the result has been + // received. + base::Lock control_lock_; + // The pid of the zygote. + pid_t pid_; + // The list of running zygote children. + std::set<pid_t> list_of_running_zygote_children_; + // The lock to guard the list of running zygote children. + base::Lock child_tracking_lock_; + int sandbox_status_; + bool have_read_sandbox_status_word_; + bool use_suid_sandbox_for_adj_oom_score_; + // Set to true when the zygote is initialized successfully. + bool init_; +}; + +} // namespace content + +#endif // CONTENT_COMMON_ZYGOTE_COMMUNICATION_H_ diff --git a/content/browser/zygote_host/zygote_handle_linux.cc b/content/browser/zygote_host/zygote_handle_linux.cc new file mode 100644 index 0000000..89adc49 --- /dev/null +++ b/content/browser/zygote_host/zygote_handle_linux.cc @@ -0,0 +1,17 @@ +// Copyright 2016 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 "content/public/browser/zygote_handle_linux.h" + +#include "content/browser/zygote_host/zygote_communication_linux.h" + +namespace content { + +ZygoteHandle CreateZygote() { + ZygoteHandle zygote = new ZygoteCommunication(); + zygote->Init(); + return zygote; +} + +} // namespace content diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc index f439e06..8b5ad76 100644 --- a/content/browser/zygote_host/zygote_host_impl_linux.cc +++ b/content/browser/zygote_host/zygote_host_impl_linux.cc @@ -4,50 +4,12 @@ #include "content/browser/zygote_host/zygote_host_impl_linux.h" -#include <errno.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "base/base_switches.h" -#include "base/command_line.h" -#include "base/environment.h" #include "base/files/file_enumerator.h" -#include "base/files/file_util.h" -#include "base/files/scoped_file.h" -#include "base/linux_util.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/scoped_ptr.h" -#include "base/metrics/histogram.h" -#include "base/metrics/sparse_histogram.h" -#include "base/path_service.h" -#include "base/posix/eintr_wrapper.h" -#include "base/posix/unix_domain_socket_linux.h" -#include "base/process/launch.h" +#include "base/process/kill.h" #include "base/process/memory.h" -#include "base/process/process_handle.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/time/time.h" -#include "build/build_config.h" -#include "content/browser/renderer_host/render_sandbox_host_linux.h" -#include "content/common/child_process_sandbox_support_impl_linux.h" -#include "content/common/zygote_commands_linux.h" #include "content/public/browser/content_browser_client.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/result_codes.h" -#include "sandbox/linux/services/credentials.h" -#include "sandbox/linux/services/namespace_sandbox.h" -#include "sandbox/linux/services/namespace_utils.h" -#include "sandbox/linux/suid/client/setuid_sandbox_host.h" #include "sandbox/linux/suid/common/sandbox.h" -#include "ui/base/ui_base_switches.h" -#include "ui/gfx/switches.h" #if defined(USE_TCMALLOC) #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" @@ -55,49 +17,18 @@ namespace content { -namespace { - -// Receive a fixed message on fd and return the sender's PID. -// Returns true if the message received matches the expected message. -bool ReceiveFixedMessage(int fd, - const char* expect_msg, - size_t expect_len, - base::ProcessId* sender_pid) { - char buf[expect_len + 1]; - std::vector<base::ScopedFD> fds_vec; - - const ssize_t len = base::UnixDomainSocket::RecvMsgWithPid( - fd, buf, sizeof(buf), &fds_vec, sender_pid); - if (static_cast<size_t>(len) != expect_len) - return false; - if (memcmp(buf, expect_msg, expect_len) != 0) - return false; - if (!fds_vec.empty()) - return false; - return true; -} - -} // namespace - // static ZygoteHost* ZygoteHost::GetInstance() { return ZygoteHostImpl::GetInstance(); } ZygoteHostImpl::ZygoteHostImpl() - : control_fd_(-1), - control_lock_(), - pid_(-1), - init_(false), - use_suid_sandbox_for_adj_oom_score_(false), + : use_suid_sandbox_for_adj_oom_score_(false), sandbox_binary_(), - have_read_sandbox_status_word_(false), - sandbox_status_(0), - child_tracking_lock_(), - list_of_running_zygote_children_(), - should_teardown_after_last_child_exits_(false) {} + zygote_pids_lock_(), + zygote_pids_() {} -ZygoteHostImpl::~ZygoteHostImpl() { TearDown(); } +ZygoteHostImpl::~ZygoteHostImpl() {} // static ZygoteHostImpl* ZygoteHostImpl::GetInstance() { @@ -105,340 +36,29 @@ ZygoteHostImpl* ZygoteHostImpl::GetInstance() { } void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { - DCHECK(!init_); - init_ = true; - - base::FilePath chrome_path; - CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); - base::CommandLine cmd_line(chrome_path); - - cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); - - int fds[2]; - CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); - CHECK(base::UnixDomainSocket::EnableReceiveProcessId(fds[0])); - base::FileHandleMappingVector fds_to_map; - fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); - - base::LaunchOptions options; - const base::CommandLine& browser_command_line = - *base::CommandLine::ForCurrentProcess(); - if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { - cmd_line.PrependWrapper( - browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); - } - // Append any switches from the browser process that need to be forwarded on - // to the zygote/renderers. - // Should this list be obtained from browser_render_process_host.cc? - static const char* kForwardSwitches[] = { - switches::kAllowSandboxDebugging, - switches::kDisableSeccompFilterSandbox, - switches::kEnableHeapProfiling, - switches::kEnableLogging, // Support, e.g., --enable-logging=stderr. - // Zygote process needs to know what resources to have loaded when it - // becomes a renderer process. - switches::kForceDeviceScaleFactor, - switches::kLoggingLevel, - switches::kNoSandbox, - switches::kPpapiInProcess, - switches::kRegisterPepperPlugins, - switches::kV, - switches::kVModule, - }; - cmd_line.CopySwitchesFrom(browser_command_line, kForwardSwitches, - arraysize(kForwardSwitches)); - - GetContentClient()->browser()->AppendExtraCommandLineSwitches(&cmd_line, -1); - - sandbox_binary_ = sandbox_cmd.c_str(); - - const bool using_namespace_sandbox = ShouldUseNamespaceSandbox(); - // A non empty sandbox_cmd means we want a SUID sandbox. - const bool using_suid_sandbox = - !sandbox_cmd.empty() && !using_namespace_sandbox; - - // Use the SUID sandbox for adjusting OOM scores when we are using the setuid - // or namespace sandbox. This is needed beacuse the processes are - // non-dumpable, so /proc/pid/oom_score_adj can only be written by root. - use_suid_sandbox_for_adj_oom_score_ = - !sandbox_binary_.empty() && using_suid_sandbox; - - // Start up the sandbox host process and get the file descriptor for the - // renderers to talk to it. - const int sfd = RenderSandboxHostLinux::GetInstance()->GetRendererSocket(); - fds_to_map.push_back(std::make_pair(sfd, GetSandboxFD())); - - base::ScopedFD dummy_fd; - if (using_suid_sandbox) { - scoped_ptr<sandbox::SetuidSandboxHost> sandbox_host( - sandbox::SetuidSandboxHost::Create()); - sandbox_host->PrependWrapper(&cmd_line); - sandbox_host->SetupLaunchOptions(&options, &fds_to_map, &dummy_fd); - sandbox_host->SetupLaunchEnvironment(); - } - - options.fds_to_remap = &fds_to_map; - base::Process process = - using_namespace_sandbox - ? sandbox::NamespaceSandbox::LaunchProcess(cmd_line, options) - : base::LaunchProcess(cmd_line, options); - CHECK(process.IsValid()) << "Failed to launch zygote process"; - - dummy_fd.reset(); - - if (using_suid_sandbox || using_namespace_sandbox) { - // The SUID sandbox will execute the zygote in a new PID namespace, and - // the main zygote process will then fork from there. Watch now our - // elaborate dance to find and validate the zygote's PID. - - // First we receive a message from the zygote boot process. - base::ProcessId boot_pid; - CHECK(ReceiveFixedMessage( - fds[0], kZygoteBootMessage, sizeof(kZygoteBootMessage), &boot_pid)); - - // Within the PID namespace, the zygote boot process thinks it's PID 1, - // but its real PID can never be 1. This gives us a reliable test that - // the kernel is translating the sender's PID to our namespace. - CHECK_GT(boot_pid, 1) - << "Received invalid process ID for zygote; kernel might be too old? " - "See crbug.com/357670 or try using --" - << switches::kDisableSetuidSandbox << " to workaround."; - - // Now receive the message that the zygote's ready to go, along with the - // main zygote process's ID. - CHECK(ReceiveFixedMessage( - fds[0], kZygoteHelloMessage, sizeof(kZygoteHelloMessage), &pid_)); - CHECK_GT(pid_, 1); - - if (process.Pid() != pid_) { - // Reap the sandbox. - base::EnsureProcessGetsReaped(process.Pid()); - } - } else { - // Not using the SUID sandbox. - // Note that ~base::Process() will reset the internal value, but there's no - // real "handle" on POSIX so that is safe. - pid_ = process.Pid(); - } - - close(fds[1]); - control_fd_ = fds[0]; - - base::Pickle pickle; - pickle.WriteInt(kZygoteCommandGetSandboxStatus); - if (!SendMessage(pickle, NULL)) - LOG(FATAL) << "Cannot communicate with zygote"; - // We don't wait for the reply. We'll read it in ReadReply. + sandbox_binary_ = sandbox_cmd; } -void ZygoteHostImpl::TearDownAfterLastChild() { - bool do_teardown = false; - { - base::AutoLock lock(child_tracking_lock_); - should_teardown_after_last_child_exits_ = true; - do_teardown = list_of_running_zygote_children_.empty(); - } - if (do_teardown) { - TearDown(); - } +void ZygoteHostImpl::AddZygotePid(pid_t pid) { + base::AutoLock lock(zygote_pids_lock_); + zygote_pids_.insert(pid); } -// Note: this is also called from the destructor. -void ZygoteHostImpl::TearDown() { - base::AutoLock lock(control_lock_); - if (control_fd_ > -1) { - // Closing the IPC channel will act as a notification to exit - // to the Zygote. - if (IGNORE_EINTR(close(control_fd_))) { - PLOG(ERROR) << "Could not close Zygote control channel."; - NOTREACHED(); - } - control_fd_ = -1; - } +bool ZygoteHostImpl::IsZygotePid(pid_t pid) { + base::AutoLock lock(zygote_pids_lock_); + return zygote_pids_.find(pid) != zygote_pids_.end(); } -void ZygoteHostImpl::ZygoteChildBorn(pid_t process) { - base::AutoLock lock(child_tracking_lock_); - bool new_element_inserted = - list_of_running_zygote_children_.insert(process).second; - DCHECK(new_element_inserted); +const std::string& ZygoteHostImpl::SandboxCommand() const { + return sandbox_binary_; } -void ZygoteHostImpl::ZygoteChildDied(pid_t process) { - bool do_teardown = false; - { - base::AutoLock lock(child_tracking_lock_); - size_t num_erased = list_of_running_zygote_children_.erase(process); - DCHECK_EQ(1U, num_erased); - do_teardown = should_teardown_after_last_child_exits_ && - list_of_running_zygote_children_.empty(); - } - if (do_teardown) { - TearDown(); - } +void ZygoteHostImpl::SetRendererSandboxStatus(int status) { + renderer_sandbox_status_ = status; } -bool ZygoteHostImpl::SendMessage(const base::Pickle& data, - const std::vector<int>* fds) { - DCHECK_NE(-1, control_fd_); - CHECK(data.size() <= kZygoteMaxMessageLength) - << "Trying to send too-large message to zygote (sending " << data.size() - << " bytes, max is " << kZygoteMaxMessageLength << ")"; - CHECK(!fds || fds->size() <= base::UnixDomainSocket::kMaxFileDescriptors) - << "Trying to send message with too many file descriptors to zygote " - << "(sending " << fds->size() << ", max is " - << base::UnixDomainSocket::kMaxFileDescriptors << ")"; - - return base::UnixDomainSocket::SendMsg(control_fd_, - data.data(), data.size(), - fds ? *fds : std::vector<int>()); -} - -ssize_t ZygoteHostImpl::ReadReply(void* buf, size_t buf_len) { - DCHECK_NE(-1, control_fd_); - // At startup we send a kZygoteCommandGetSandboxStatus request to the zygote, - // but don't wait for the reply. Thus, the first time that we read from the - // zygote, we get the reply to that request. - if (!have_read_sandbox_status_word_) { - if (HANDLE_EINTR(read(control_fd_, &sandbox_status_, - sizeof(sandbox_status_))) != - sizeof(sandbox_status_)) { - return -1; - } - - have_read_sandbox_status_word_ = true; - UMA_HISTOGRAM_SPARSE_SLOWLY("Linux.SandboxStatus", sandbox_status_); - } - - return HANDLE_EINTR(read(control_fd_, buf, buf_len)); -} - -pid_t ZygoteHostImpl::ForkRequest(const std::vector<std::string>& argv, - scoped_ptr<FileDescriptorInfo> mapping, - const std::string& process_type) { - DCHECK(init_); - base::Pickle pickle; - - int raw_socks[2]; - PCHECK(0 == socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks)); - base::ScopedFD my_sock(raw_socks[0]); - base::ScopedFD peer_sock(raw_socks[1]); - CHECK(base::UnixDomainSocket::EnableReceiveProcessId(my_sock.get())); - - pickle.WriteInt(kZygoteCommandFork); - pickle.WriteString(process_type); - pickle.WriteInt(argv.size()); - for (std::vector<std::string>::const_iterator - i = argv.begin(); i != argv.end(); ++i) - pickle.WriteString(*i); - - // Fork requests contain one file descriptor for the PID oracle, and one - // more for each file descriptor mapping for the child process. - const size_t num_fds_to_send = 1 + mapping->GetMappingSize(); - pickle.WriteInt(num_fds_to_send); - - std::vector<int> fds; - - // First FD to send is peer_sock. - // TODO(morrita): Ideally, this should be part of the mapping so that - // FileDescriptorInfo can manages its lifetime. - fds.push_back(peer_sock.get()); - - // The rest come from mapping. - for (size_t i = 0; i < mapping->GetMappingSize(); ++i) { - pickle.WriteUInt32(mapping->GetIDAt(i)); - fds.push_back(mapping->GetFDAt(i)); - } - - // Sanity check that we've populated |fds| correctly. - DCHECK_EQ(num_fds_to_send, fds.size()); - - pid_t pid; - { - base::AutoLock lock(control_lock_); - if (!SendMessage(pickle, &fds)) - return base::kNullProcessHandle; - mapping.reset(); - peer_sock.reset(); - - { - char buf[sizeof(kZygoteChildPingMessage) + 1]; - std::vector<base::ScopedFD> recv_fds; - base::ProcessId real_pid; - - ssize_t n = base::UnixDomainSocket::RecvMsgWithPid( - my_sock.get(), buf, sizeof(buf), &recv_fds, &real_pid); - if (n != sizeof(kZygoteChildPingMessage) || - 0 != memcmp(buf, - kZygoteChildPingMessage, - sizeof(kZygoteChildPingMessage))) { - // Zygote children should still be trustworthy when they're supposed to - // ping us, so something's broken if we don't receive a valid ping. - LOG(ERROR) << "Did not receive ping from zygote child"; - NOTREACHED(); - real_pid = -1; - } - my_sock.reset(); - - // Always send PID back to zygote. - base::Pickle pid_pickle; - pid_pickle.WriteInt(kZygoteCommandForkRealPID); - pid_pickle.WriteInt(real_pid); - if (!SendMessage(pid_pickle, NULL)) - return base::kNullProcessHandle; - } - - // Read the reply, which pickles the PID and an optional UMA enumeration. - static const unsigned kMaxReplyLength = 2048; - char buf[kMaxReplyLength]; - const ssize_t len = ReadReply(buf, sizeof(buf)); - - base::Pickle reply_pickle(buf, len); - base::PickleIterator iter(reply_pickle); - if (len <= 0 || !iter.ReadInt(&pid)) - return base::kNullProcessHandle; - - // If there is a nonempty UMA name string, then there is a UMA - // enumeration to record. - std::string uma_name; - int uma_sample; - int uma_boundary_value; - if (iter.ReadString(&uma_name) && - !uma_name.empty() && - iter.ReadInt(&uma_sample) && - iter.ReadInt(&uma_boundary_value)) { - // We cannot use the UMA_HISTOGRAM_ENUMERATION macro here, - // because that's only for when the name is the same every time. - // Here we're using whatever name we got from the other side. - // But since it's likely that the same one will be used repeatedly - // (even though it's not guaranteed), we cache it here. - static base::HistogramBase* uma_histogram; - if (!uma_histogram || uma_histogram->histogram_name() != uma_name) { - uma_histogram = base::LinearHistogram::FactoryGet( - uma_name, 1, - uma_boundary_value, - uma_boundary_value + 1, - base::HistogramBase::kUmaTargetedHistogramFlag); - } - uma_histogram->Add(uma_sample); - } - - if (pid <= 0) - return base::kNullProcessHandle; - } - -#if !defined(OS_OPENBSD) - // This is just a starting score for a renderer or extension (the - // only types of processes that will be started this way). It will - // get adjusted as time goes on. (This is the same value as - // chrome::kLowestRendererOomScore in chrome/chrome_constants.h, but - // that's not something we can include here.) - const int kLowestRendererOomScore = 300; - AdjustRendererOOMScore(pid, kLowestRendererOomScore); -#endif - - ZygoteChildBorn(pid); - return pid; +int ZygoteHostImpl::GetRendererSandboxStatus() const { + return renderer_sandbox_status_; } #if !defined(OS_OPENBSD) @@ -514,92 +134,4 @@ void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid, } #endif -void ZygoteHostImpl::EnsureProcessTerminated(pid_t process) { - DCHECK(init_); - base::Pickle pickle; - - pickle.WriteInt(kZygoteCommandReap); - pickle.WriteInt(process); - if (!SendMessage(pickle, NULL)) - LOG(ERROR) << "Failed to send Reap message to zygote"; - ZygoteChildDied(process); -} - -base::TerminationStatus ZygoteHostImpl::GetTerminationStatus( - base::ProcessHandle handle, - bool known_dead, - int* exit_code) { - DCHECK(init_); - base::Pickle pickle; - pickle.WriteInt(kZygoteCommandGetTerminationStatus); - pickle.WriteBool(known_dead); - pickle.WriteInt(handle); - - static const unsigned kMaxMessageLength = 128; - char buf[kMaxMessageLength]; - ssize_t len; - { - base::AutoLock lock(control_lock_); - if (!SendMessage(pickle, NULL)) - LOG(ERROR) << "Failed to send GetTerminationStatus message to zygote"; - len = ReadReply(buf, sizeof(buf)); - } - - // Set this now to handle the error cases. - if (exit_code) - *exit_code = RESULT_CODE_NORMAL_EXIT; - int status = base::TERMINATION_STATUS_NORMAL_TERMINATION; - - if (len == -1) { - LOG(WARNING) << "Error reading message from zygote: " << errno; - } else if (len == 0) { - LOG(WARNING) << "Socket closed prematurely."; - } else { - base::Pickle read_pickle(buf, len); - int tmp_status, tmp_exit_code; - base::PickleIterator iter(read_pickle); - if (!iter.ReadInt(&tmp_status) || !iter.ReadInt(&tmp_exit_code)) { - LOG(WARNING) - << "Error parsing GetTerminationStatus response from zygote."; - } else { - if (exit_code) - *exit_code = tmp_exit_code; - status = tmp_status; - } - } - - if (status != base::TERMINATION_STATUS_STILL_RUNNING) { - ZygoteChildDied(handle); - } - return static_cast<base::TerminationStatus>(status); -} - -pid_t ZygoteHostImpl::GetPid() const { - return pid_; -} - -int ZygoteHostImpl::GetSandboxStatus() const { - if (have_read_sandbox_status_word_) - return sandbox_status_; - return 0; -} - -bool ZygoteHostImpl::ShouldUseNamespaceSandbox() { - const base::CommandLine& command_line = - *base::CommandLine::ForCurrentProcess(); - if (command_line.HasSwitch(switches::kNoSandbox)) { - return false; - } - - if (command_line.HasSwitch(switches::kDisableNamespaceSandbox)) { - return false; - } - - if (!sandbox::Credentials::CanCreateProcessInNewUserNS()) { - return false; - } - - return true; -} - } // namespace content diff --git a/content/browser/zygote_host/zygote_host_impl_linux.h b/content/browser/zygote_host/zygote_host_impl_linux.h index 6793f73..e640a99 100644 --- a/content/browser/zygote_host/zygote_host_impl_linux.h +++ b/content/browser/zygote_host/zygote_host_impl_linux.h @@ -5,16 +5,13 @@ #ifndef CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_ #define CONTENT_BROWSER_ZYGOTE_HOST_ZYGOTE_HOST_IMPL_LINUX_H_ -#include <stddef.h> +#include <sys/types.h> #include <set> #include <string> -#include <vector> -#include "base/pickle.h" -#include "base/process/kill.h" +#include "base/process/process_handle.h" #include "base/synchronization/lock.h" -#include "content/public/browser/file_descriptor_info.h" #include "content/public/browser/zygote_host_linux.h" namespace base { @@ -31,35 +28,17 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost { void Init(const std::string& sandbox_cmd); - // After the last known Zygote child exits, notify the Zygote to exit. - void TearDownAfterLastChild(); - - // Tries to start a process of type indicated by process_type. - // Returns its pid on success, otherwise - // base::kNullProcessHandle; - pid_t ForkRequest(const std::vector<std::string>& command_line, - scoped_ptr<FileDescriptorInfo> mapping, - const std::string& process_type); - void EnsureProcessTerminated(pid_t process); - - // Get the termination status (and, optionally, the exit code) of - // the process. |exit_code| is set to the exit code of the child - // process. (|exit_code| may be NULL.) - // Unfortunately the Zygote can not accurately figure out if a process - // is already dead without waiting synchronously for it. - // |known_dead| should be set to true when we already know that the process - // is dead. When |known_dead| is false, processes could be seen as - // still running, even when they're not. When |known_dead| is true, the - // process will be SIGKILL-ed first (which should have no effect if it was - // really dead). This is to prevent a waiting waitpid() from blocking in - // a single-threaded Zygote. See crbug.com/157458. - base::TerminationStatus GetTerminationStatus(base::ProcessHandle handle, - bool known_dead, - int* exit_code); - - // ZygoteHost implementation: - pid_t GetPid() const override; - int GetSandboxStatus() const override; + // Retrieves the sandbox command passed into Init(); + const std::string& SandboxCommand() const; + + // Tells the ZygoteHost the PIDs of all the zygotes. + void AddZygotePid(pid_t pid); + + // Returns whether or not this pid is the pid of a zygote. + bool IsZygotePid(pid_t pid) override; + + void SetRendererSandboxStatus(int status); + int GetRendererSandboxStatus() const override; void AdjustRendererOOMScore(base::ProcessHandle process_handle, int score) override; @@ -69,42 +48,13 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost { ZygoteHostImpl(); ~ZygoteHostImpl() override; - // Notify the Zygote to exit immediately. This object should not be - // used afterwards. - void TearDown(); - - // Should be called every time a Zygote child is born. - void ZygoteChildBorn(pid_t process); - - // Should be called every time a Zygote child died. - void ZygoteChildDied(pid_t process); - - // Sends |data| to the zygote via |control_fd_|. If |fds| is non-NULL, the - // included file descriptors will also be passed. The caller is responsible - // for acquiring |control_lock_|. - bool SendMessage(const base::Pickle& data, const std::vector<int>* fds); - - ssize_t ReadReply(void* buf, size_t buflen); - - // Whether we should use the namespace sandbox instead of the setuid sandbox. - bool ShouldUseNamespaceSandbox(); - - int control_fd_; // the socket to the zygote - // A lock protecting all communication with the zygote. This lock must be - // acquired before sending a command and released after the result has been - // received. - base::Lock control_lock_; - pid_t pid_; - bool init_; + int renderer_sandbox_status_; bool use_suid_sandbox_for_adj_oom_score_; std::string sandbox_binary_; - bool have_read_sandbox_status_word_; - int sandbox_status_; - // A lock protecting list_of_running_zygote_children_ and - // should_teardown_after_last_child_exits_. - base::Lock child_tracking_lock_; - std::set<pid_t> list_of_running_zygote_children_; - bool should_teardown_after_last_child_exits_; + // This lock protects the |zygote_pids_| set. + base::Lock zygote_pids_lock_; + // This is a set of PIDs representing all the running zygotes. + std::set<pid_t> zygote_pids_; }; } // namespace content diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 4369cda..26d3ab5 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -302,6 +302,7 @@ 'public/browser/worker_service.h', 'public/browser/worker_service_observer.h', 'public/browser/zoom_level_delegate.h', + 'public/browser/zygote_handle_linux.h', 'public/browser/zygote_host_linux.h', ], 'private_browser_sources': [ @@ -1572,6 +1573,9 @@ 'browser/webui/web_ui_impl.cc', 'browser/webui/web_ui_impl.h', 'browser/webui/web_ui_message_handler.cc', + 'browser/zygote_host/zygote_communication_linux.h', + 'browser/zygote_host/zygote_communication_linux.cc', + 'browser/zygote_host/zygote_handle_linux.cc', 'browser/zygote_host/zygote_host_impl_linux.cc', 'browser/zygote_host/zygote_host_impl_linux.h', 'zygote/zygote_linux.cc', diff --git a/content/content_common.gypi b/content/content_common.gypi index 66edae2..1c686bf 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -161,6 +161,7 @@ 'public/common/webplugininfo.h', 'public/common/window_container_type.cc', 'public/common/window_container_type.h', + 'public/common/zygote_handle.h', 'public/common/zygote_fork_delegate_linux.h', ], 'private_common_sources': [ diff --git a/content/public/browser/zygote_handle_linux.h b/content/public/browser/zygote_handle_linux.h new file mode 100644 index 0000000..b644c51 --- /dev/null +++ b/content/public/browser/zygote_handle_linux.h @@ -0,0 +1,21 @@ +// Copyright 2016 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_PUBLIC_BROWSER_ZYGOTE_HANDLE_LINUX_H_ +#define CONTENT_PUBLIC_BROWSER_ZYGOTE_HANDLE_LINUX_H_ + +#include <cstddef> + +#include "content/common/content_export.h" +#include "content/public/common/zygote_handle.h" + +namespace content { + +// Allocates and initializes a zygote process, and returns the +// ZygoteHandle used to communicate with it. +CONTENT_EXPORT ZygoteHandle CreateZygote(); + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_ZYGOTE_HANDLE_LINUX_H_ diff --git a/content/public/browser/zygote_host_linux.h b/content/public/browser/zygote_host_linux.h index ebe5ee7..e12f8db2 100644 --- a/content/public/browser/zygote_host_linux.h +++ b/content/public/browser/zygote_host_linux.h @@ -14,7 +14,7 @@ namespace content { // http://code.google.com/p/chromium/wiki/LinuxZygote -// The zygote host is the interface, in the browser process, to the zygote +// The zygote host is an interface, in the browser process, to the zygote // process. class ZygoteHost { public: @@ -24,11 +24,11 @@ class ZygoteHost { virtual ~ZygoteHost() {} // Returns the pid of the Zygote process. - virtual pid_t GetPid() const = 0; + virtual bool IsZygotePid(pid_t pid) = 0; // Returns an int which is a bitmask of kSandboxLinux* values. Only valid // after the first render has been forked. - virtual int GetSandboxStatus() const = 0; + virtual int GetRendererSandboxStatus() const = 0; // Adjust the OOM score of the given renderer's PID. The allowed // range for the score is [0, 1000], where higher values are more diff --git a/content/public/common/sandboxed_process_launcher_delegate.cc b/content/public/common/sandboxed_process_launcher_delegate.cc index e0204c4..5435816 100644 --- a/content/public/common/sandboxed_process_launcher_delegate.cc +++ b/content/public/common/sandboxed_process_launcher_delegate.cc @@ -26,9 +26,11 @@ bool SandboxedProcessLauncherDelegate::PreSpawnTarget( } #elif(OS_POSIX) -bool SandboxedProcessLauncherDelegate::ShouldUseZygote() { - return false; +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) +ZygoteHandle* SandboxedProcessLauncherDelegate::GetZygote() { + return nullptr; } +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) base::EnvironmentMap SandboxedProcessLauncherDelegate::GetEnvironment() { return base::EnvironmentMap(); diff --git a/content/public/common/sandboxed_process_launcher_delegate.h b/content/public/common/sandboxed_process_launcher_delegate.h index a3aa4e0..9dfcb326 100644 --- a/content/public/common/sandboxed_process_launcher_delegate.h +++ b/content/public/common/sandboxed_process_launcher_delegate.h @@ -5,12 +5,15 @@ #ifndef CONTENT_PUBLIC_COMMON_SANDBOXED_PROCESS_LAUNCHER_DELEGATE_H_ #define CONTENT_PUBLIC_COMMON_SANDBOXED_PROCESS_LAUNCHER_DELEGATE_H_ +#include <cstddef> + #include "base/environment.h" #include "base/files/scoped_file.h" #include "base/process/process.h" #include "build/build_config.h" #include "content/common/content_export.h" #include "content/public/common/sandbox_type.h" +#include "content/public/common/zygote_handle.h" namespace base { class FilePath; @@ -51,8 +54,10 @@ class CONTENT_EXPORT SandboxedProcessLauncherDelegate { virtual void PostSpawnTarget(base::ProcessHandle process) {} #elif defined(OS_POSIX) +#if !defined(OS_MACOSX) && !defined(OS_ANDROID) // Override this to return true to use the setuid sandbox. - virtual bool ShouldUseZygote(); + virtual ZygoteHandle* GetZygote(); +#endif // !defined(OS_MACOSX) && !defined(OS_ANDROID) // Override this if the process needs a non-empty environment map. virtual base::EnvironmentMap GetEnvironment(); diff --git a/content/public/common/zygote_handle.h b/content/public/common/zygote_handle.h new file mode 100644 index 0000000..9aa07c4 --- /dev/null +++ b/content/public/common/zygote_handle.h @@ -0,0 +1,23 @@ +// Copyright 2016 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_PUBLIC_COMMON_ZYGOTE_HANDLE_H_ +#define CONTENT_PUBLIC_COMMON_ZYGOTE_HANDLE_H_ + +#include <cstddef> + +#include "build/build_config.h" + +namespace content { + +#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX) +class ZygoteCommunication; +using ZygoteHandle = ZygoteCommunication*; +#else +using ZygoteHandle = std::nullptr_t; +#endif + +} // namespace content + +#endif // CONTENT_PUBLIC_COMMON_ZYGOTE_HANDLE_H_ |