diff options
author | kerrnel <kerrnel@chromium.org> | 2016-01-20 13:11:32 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-20 21:12:37 +0000 |
commit | 3c1e16b490255119b6f70f94d1716645e897b185 (patch) | |
tree | aeba7152bbdb97e03b5d6365ec654728bceb9d36 | |
parent | 03af3e520814e764eba70ce6512f0a0f98e3e18e (diff) | |
download | chromium_src-3c1e16b490255119b6f70f94d1716645e897b185.zip chromium_src-3c1e16b490255119b6f70f94d1716645e897b185.tar.gz chromium_src-3c1e16b490255119b6f70f94d1716645e897b185.tar.bz2 |
Have each SandboxedProcessLauncherDelegate maintain a zygote.
To improve component updates of PPAPI plugins, Chrome needs multiple zygotes.
This will allow the PPAPI zygote to be recreated when a plugin is updated.
This CL allows Chrome to maintain a zygote for each process type by having
each SandboxedProcessLauncherDelegate maintain a class which can communicate
with its respective zygote. This CL will be followed up with work to allow
customization of zygotes, which will give Chrome the improve component update
experience.
BUG=569191
Review URL: https://codereview.chromium.org/1532423003
Cr-Commit-Position: refs/heads/master@{#370488}
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_ |