|  | // Copyright (c) 2012 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/common/child_process_host_impl.h" | 
|  |  | 
|  | #include <limits> | 
|  |  | 
|  | #include "base/atomic_sequence_num.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/files/file_path.h" | 
|  | #include "base/hash.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/metrics/histogram_macros.h" | 
|  | #include "base/numerics/safe_math.h" | 
|  | #include "base/path_service.h" | 
|  | #include "base/process/process_metrics.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "base/synchronization/lock.h" | 
|  | #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | 
|  | #include "build/build_config.h" | 
|  | #include "content/common/child_process_messages.h" | 
|  | #include "content/public/common/child_process_host_delegate.h" | 
|  | #include "content/public/common/content_paths.h" | 
|  | #include "content/public/common/content_switches.h" | 
|  | #include "gpu/ipc/client/gpu_memory_buffer_impl_shared_memory.h" | 
|  | #include "ipc/attachment_broker.h" | 
|  | #include "ipc/attachment_broker_privileged.h" | 
|  | #include "ipc/ipc.mojom.h" | 
|  | #include "ipc/ipc_channel.h" | 
|  | #include "ipc/ipc_channel_mojo.h" | 
|  | #include "ipc/ipc_logging.h" | 
|  | #include "ipc/message_filter.h" | 
|  | #include "mojo/edk/embedder/embedder.h" | 
|  | #include "services/shell/public/cpp/interface_provider.h" | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | #include "base/linux_util.h" | 
|  | #elif defined(OS_WIN) | 
|  | #include "content/common/font_cache_dispatcher_win.h" | 
|  | #endif  // OS_LINUX | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Global atomic to generate child process unique IDs. | 
|  | base::StaticAtomicSequenceNumber g_unique_id; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | int ChildProcessHost::kInvalidUniqueID = -1; | 
|  |  | 
|  | uint64_t ChildProcessHost::kBrowserTracingProcessId = | 
|  | std::numeric_limits<uint64_t>::max(); | 
|  |  | 
|  | // static | 
|  | ChildProcessHost* ChildProcessHost::Create(ChildProcessHostDelegate* delegate) { | 
|  | return new ChildProcessHostImpl(delegate); | 
|  | } | 
|  |  | 
|  | // static | 
|  | base::FilePath ChildProcessHost::GetChildPath(int flags) { | 
|  | base::FilePath child_path; | 
|  |  | 
|  | child_path = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( | 
|  | switches::kBrowserSubprocessPath); | 
|  |  | 
|  | #if defined(OS_LINUX) | 
|  | // Use /proc/self/exe rather than our known binary path so updates | 
|  | // can't swap out the binary from underneath us. | 
|  | // When running under Valgrind, forking /proc/self/exe ends up forking the | 
|  | // Valgrind executable, which then crashes. However, it's almost safe to | 
|  | // assume that the updates won't happen while testing with Valgrind tools. | 
|  | if (child_path.empty() && flags & CHILD_ALLOW_SELF && !RunningOnValgrind()) | 
|  | child_path = base::FilePath(base::kProcSelfExe); | 
|  | #endif | 
|  |  | 
|  | // On most platforms, the child executable is the same as the current | 
|  | // executable. | 
|  | if (child_path.empty()) | 
|  | PathService::Get(CHILD_PROCESS_EXE, &child_path); | 
|  | return child_path; | 
|  | } | 
|  |  | 
|  | ChildProcessHostImpl::ChildProcessHostImpl(ChildProcessHostDelegate* delegate) | 
|  | : delegate_(delegate), | 
|  | opening_channel_(false) { | 
|  | #if defined(OS_WIN) | 
|  | AddFilter(new FontCacheDispatcher()); | 
|  | #endif | 
|  |  | 
|  | #if USE_ATTACHMENT_BROKER | 
|  | #if defined(OS_MACOSX) | 
|  | // On Mac, the privileged AttachmentBroker needs a reference to the Mach port | 
|  | // Provider, which is only available in the chrome/ module. The attachment | 
|  | // broker must already be created. | 
|  | DCHECK(IPC::AttachmentBroker::GetGlobal()); | 
|  | #else | 
|  | // Construct the privileged attachment broker early in the life cycle of a | 
|  | // child process. | 
|  | IPC::AttachmentBrokerPrivileged::CreateBrokerIfNeeded(); | 
|  | #endif  // defined(OS_MACOSX) | 
|  | #endif  // USE_ATTACHMENT_BROKER | 
|  | } | 
|  |  | 
|  | ChildProcessHostImpl::~ChildProcessHostImpl() { | 
|  | // If a channel was never created than it wasn't registered and the filters | 
|  | // weren't notified. For the sake of symmetry don't call the matching teardown | 
|  | // functions. This is analogous to how RenderProcessHostImpl handles things. | 
|  | if (!channel_) | 
|  | return; | 
|  |  | 
|  | #if USE_ATTACHMENT_BROKER | 
|  | IPC::AttachmentBroker::GetGlobal()->DeregisterCommunicationChannel( | 
|  | channel_.get()); | 
|  | #endif | 
|  | for (size_t i = 0; i < filters_.size(); ++i) { | 
|  | filters_[i]->OnChannelClosing(); | 
|  | filters_[i]->OnFilterRemoved(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::AddFilter(IPC::MessageFilter* filter) { | 
|  | filters_.push_back(filter); | 
|  |  | 
|  | if (channel_) | 
|  | filter->OnFilterAdded(channel_.get()); | 
|  | } | 
|  |  | 
|  | shell::InterfaceProvider* ChildProcessHostImpl::GetRemoteInterfaces() { | 
|  | return delegate_->GetRemoteInterfaces(); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::ForceShutdown() { | 
|  | Send(new ChildProcessMsg_Shutdown()); | 
|  | } | 
|  |  | 
|  | std::string ChildProcessHostImpl::CreateChannelMojo( | 
|  | const std::string& child_token) { | 
|  | DCHECK(channel_id_.empty()); | 
|  | channel_id_ = mojo::edk::GenerateRandomToken(); | 
|  | mojo::ScopedMessagePipeHandle host_handle = | 
|  | mojo::edk::CreateParentMessagePipe(channel_id_, child_token); | 
|  | channel_ = IPC::ChannelMojo::Create(std::move(host_handle), | 
|  | IPC::Channel::MODE_SERVER, this); | 
|  | if (!channel_ || !InitChannel()) | 
|  | return std::string(); | 
|  |  | 
|  | return channel_id_; | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::CreateChannelMojo() { | 
|  | // TODO(rockot): Remove |channel_id_| once this is the only code path by which | 
|  | // the Channel is created. For now it serves to at least mutually exclude | 
|  | // different CreateChannel* calls. | 
|  | DCHECK(channel_id_.empty()); | 
|  | channel_id_ = "ChannelMojo"; | 
|  |  | 
|  | shell::InterfaceProvider* remote_interfaces = GetRemoteInterfaces(); | 
|  | DCHECK(remote_interfaces); | 
|  |  | 
|  | IPC::mojom::ChannelBootstrapPtr bootstrap; | 
|  | remote_interfaces->GetInterface(&bootstrap); | 
|  | channel_ = IPC::ChannelMojo::Create(bootstrap.PassInterface().PassHandle(), | 
|  | IPC::Channel::MODE_SERVER, this); | 
|  | DCHECK(channel_); | 
|  |  | 
|  | bool initialized = InitChannel(); | 
|  | DCHECK(initialized); | 
|  | } | 
|  |  | 
|  | std::string ChildProcessHostImpl::CreateChannel() { | 
|  | DCHECK(channel_id_.empty()); | 
|  | channel_id_ = IPC::Channel::GenerateVerifiedChannelID(std::string()); | 
|  | channel_ = IPC::Channel::CreateServer(channel_id_, this); | 
|  | if (!channel_ || !InitChannel()) | 
|  | return std::string(); | 
|  |  | 
|  | return channel_id_; | 
|  | } | 
|  |  | 
|  | bool ChildProcessHostImpl::InitChannel() { | 
|  | #if USE_ATTACHMENT_BROKER | 
|  | IPC::AttachmentBroker::GetGlobal()->RegisterCommunicationChannel( | 
|  | channel_.get(), base::MessageLoopForIO::current()->task_runner()); | 
|  | #endif | 
|  | if (!channel_->Connect()) { | 
|  | #if USE_ATTACHMENT_BROKER | 
|  | IPC::AttachmentBroker::GetGlobal()->DeregisterCommunicationChannel( | 
|  | channel_.get()); | 
|  | #endif | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < filters_.size(); ++i) | 
|  | filters_[i]->OnFilterAdded(channel_.get()); | 
|  |  | 
|  | // Make sure these messages get sent first. | 
|  | #if defined(IPC_MESSAGE_LOG_ENABLED) | 
|  | bool enabled = IPC::Logging::GetInstance()->Enabled(); | 
|  | Send(new ChildProcessMsg_SetIPCLoggingEnabled(enabled)); | 
|  | #endif | 
|  |  | 
|  | opening_channel_ = true; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool ChildProcessHostImpl::IsChannelOpening() { | 
|  | return opening_channel_; | 
|  | } | 
|  |  | 
|  | #if defined(OS_POSIX) | 
|  | base::ScopedFD ChildProcessHostImpl::TakeClientFileDescriptor() { | 
|  | return channel_->TakeClientFileDescriptor(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | bool ChildProcessHostImpl::Send(IPC::Message* message) { | 
|  | if (!channel_) { | 
|  | delete message; | 
|  | return false; | 
|  | } | 
|  | return channel_->Send(message); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::AllocateSharedMemory( | 
|  | size_t buffer_size, base::ProcessHandle child_process_handle, | 
|  | base::SharedMemoryHandle* shared_memory_handle) { | 
|  | base::SharedMemory shared_buf; | 
|  | if (!shared_buf.CreateAnonymous(buffer_size)) { | 
|  | *shared_memory_handle = base::SharedMemory::NULLHandle(); | 
|  | NOTREACHED() << "Cannot create shared memory buffer"; | 
|  | return; | 
|  | } | 
|  | shared_buf.GiveToProcess(child_process_handle, shared_memory_handle); | 
|  | } | 
|  |  | 
|  | int ChildProcessHostImpl::GenerateChildProcessUniqueId() { | 
|  | // This function must be threadsafe. | 
|  | // | 
|  | // Historically, this function returned ids started with 1, so in several | 
|  | // places in the code a value of 0 (rather than kInvalidUniqueID) was used as | 
|  | // an invalid value. So we retain those semantics. | 
|  | int id = g_unique_id.GetNext() + 1; | 
|  |  | 
|  | CHECK_NE(0, id); | 
|  | CHECK_NE(kInvalidUniqueID, id); | 
|  |  | 
|  | return id; | 
|  | } | 
|  |  | 
|  | uint64_t ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId( | 
|  | int child_process_id) { | 
|  | // In single process mode, all the children are hosted in the same process, | 
|  | // therefore the generated memory dump guids should not be conditioned by the | 
|  | // child process id. The clients need not be aware of SPM and the conversion | 
|  | // takes care of the SPM special case while translating child process ids to | 
|  | // tracing process ids. | 
|  | if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 
|  | switches::kSingleProcess)) | 
|  | return ChildProcessHost::kBrowserTracingProcessId; | 
|  |  | 
|  | // The hash value is incremented so that the tracing id is never equal to | 
|  | // MemoryDumpManager::kInvalidTracingProcessId. | 
|  | return static_cast<uint64_t>( | 
|  | base::Hash(reinterpret_cast<const char*>(&child_process_id), | 
|  | sizeof(child_process_id))) + | 
|  | 1; | 
|  | } | 
|  |  | 
|  | bool ChildProcessHostImpl::OnMessageReceived(const IPC::Message& msg) { | 
|  | #ifdef IPC_MESSAGE_LOG_ENABLED | 
|  | IPC::Logging* logger = IPC::Logging::GetInstance(); | 
|  | if (msg.type() == IPC_LOGGING_ID) { | 
|  | logger->OnReceivedLoggingMessage(msg); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (logger->Enabled()) | 
|  | logger->OnPreDispatchMessage(msg); | 
|  | #endif | 
|  |  | 
|  | bool handled = false; | 
|  | for (size_t i = 0; i < filters_.size(); ++i) { | 
|  | if (filters_[i]->OnMessageReceived(msg)) { | 
|  | handled = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!handled) { | 
|  | handled = true; | 
|  | IPC_BEGIN_MESSAGE_MAP(ChildProcessHostImpl, msg) | 
|  | IPC_MESSAGE_HANDLER(ChildProcessHostMsg_ShutdownRequest, | 
|  | OnShutdownRequest) | 
|  | // NB: The SyncAllocateSharedMemory, SyncAllocateGpuMemoryBuffer, and | 
|  | // DeletedGpuMemoryBuffer IPCs are handled here for non-renderer child | 
|  | // processes. For renderer processes, they are handled in | 
|  | // RenderMessageFilter. | 
|  | IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateSharedMemory, | 
|  | OnAllocateSharedMemory) | 
|  | IPC_MESSAGE_HANDLER(ChildProcessHostMsg_SyncAllocateGpuMemoryBuffer, | 
|  | OnAllocateGpuMemoryBuffer) | 
|  | IPC_MESSAGE_HANDLER(ChildProcessHostMsg_DeletedGpuMemoryBuffer, | 
|  | OnDeletedGpuMemoryBuffer) | 
|  | IPC_MESSAGE_UNHANDLED(handled = false) | 
|  | IPC_END_MESSAGE_MAP() | 
|  |  | 
|  | if (!handled) | 
|  | handled = delegate_->OnMessageReceived(msg); | 
|  | } | 
|  |  | 
|  | #ifdef IPC_MESSAGE_LOG_ENABLED | 
|  | if (logger->Enabled()) | 
|  | logger->OnPostDispatchMessage(msg, channel_id_); | 
|  | #endif | 
|  | return handled; | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnChannelConnected(int32_t peer_pid) { | 
|  | if (!peer_process_.IsValid()) { | 
|  | peer_process_ = base::Process::OpenWithExtraPrivileges(peer_pid); | 
|  | if (!peer_process_.IsValid()) | 
|  | peer_process_ = delegate_->GetProcess().Duplicate(); | 
|  | DCHECK(peer_process_.IsValid()); | 
|  | } | 
|  | opening_channel_ = false; | 
|  | delegate_->OnChannelConnected(peer_pid); | 
|  | for (size_t i = 0; i < filters_.size(); ++i) | 
|  | filters_[i]->OnChannelConnected(peer_pid); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnChannelError() { | 
|  | opening_channel_ = false; | 
|  | delegate_->OnChannelError(); | 
|  |  | 
|  | for (size_t i = 0; i < filters_.size(); ++i) | 
|  | filters_[i]->OnChannelError(); | 
|  |  | 
|  | // This will delete host_, which will also destroy this! | 
|  | delegate_->OnChildDisconnected(); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnBadMessageReceived(const IPC::Message& message) { | 
|  | delegate_->OnBadMessageReceived(message); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnAllocateSharedMemory( | 
|  | uint32_t buffer_size, | 
|  | base::SharedMemoryHandle* handle) { | 
|  | AllocateSharedMemory(buffer_size, peer_process_.Handle(), handle); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnShutdownRequest() { | 
|  | if (delegate_->CanShutdown()) | 
|  | Send(new ChildProcessMsg_Shutdown()); | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnAllocateGpuMemoryBuffer( | 
|  | gfx::GpuMemoryBufferId id, | 
|  | uint32_t width, | 
|  | uint32_t height, | 
|  | gfx::BufferFormat format, | 
|  | gfx::BufferUsage usage, | 
|  | gfx::GpuMemoryBufferHandle* handle) { | 
|  | // TODO(reveman): Add support for other types of GpuMemoryBuffers. | 
|  |  | 
|  | // AllocateForChildProcess() will check if |width| and |height| are valid | 
|  | // and handle failure in a controlled way when not. We just need to make | 
|  | // sure |usage| is supported here. | 
|  | if (gpu::GpuMemoryBufferImplSharedMemory::IsUsageSupported(usage)) { | 
|  | *handle = gpu::GpuMemoryBufferImplSharedMemory::AllocateForChildProcess( | 
|  | id, gfx::Size(width, height), format, peer_process_.Handle()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void ChildProcessHostImpl::OnDeletedGpuMemoryBuffer( | 
|  | gfx::GpuMemoryBufferId id, | 
|  | const gpu::SyncToken& sync_token) { | 
|  | // Note: Nothing to do here as ownership of shared memory backed | 
|  | // GpuMemoryBuffers is passed with IPC. | 
|  | } | 
|  |  | 
|  | }  // namespace content |