1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/common/child_process_host_impl.h"
6 
7 #include <limits>
8 
9 #include "base/atomic_sequence_num.h"
10 #include "base/clang_profiling_buildflags.h"
11 #include "base/command_line.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/hash/hash.h"
15 #include "base/logging.h"
16 #include "base/memory/ptr_util.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "base/numerics/safe_math.h"
19 #include "base/path_service.h"
20 #include "base/process/process_metrics.h"
21 #include "base/rand_util.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/synchronization/lock.h"
24 #include "base/threading/thread_task_runner_handle.h"
25 #include "build/build_config.h"
26 #include "content/public/common/child_process_host_delegate.h"
27 #include "content/public/common/content_paths.h"
28 #include "content/public/common/content_switches.h"
29 #include "ipc/ipc.mojom.h"
30 #include "ipc/ipc_channel.h"
31 #include "ipc/ipc_channel_mojo.h"
32 #include "ipc/ipc_logging.h"
33 #include "ipc/message_filter.h"
34 #include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
35 #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h"
36 #include "services/service_manager/public/cpp/interface_provider.h"
37 
38 #if defined(OS_LINUX)
39 #include "base/linux_util.h"
40 #elif defined(OS_MACOSX)
41 #include "base/mac/foundation_util.h"
42 #include "content/common/mac_helpers.h"
43 #endif  // OS_LINUX
44 
45 #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
46 #include "content/common/profiling_utils.h"
47 #endif
48 
49 namespace {
50 
51 // Global atomic to generate child process unique IDs.
52 base::AtomicSequenceNumber g_unique_id;
53 
54 }  // namespace
55 
56 namespace content {
57 
58 // static
Create(ChildProcessHostDelegate * delegate,IpcMode ipc_mode)59 std::unique_ptr<ChildProcessHost> ChildProcessHost::Create(
60     ChildProcessHostDelegate* delegate,
61     IpcMode ipc_mode) {
62   return base::WrapUnique(new ChildProcessHostImpl(delegate, ipc_mode));
63 }
64 
65 // static
GetChildPath(int flags)66 base::FilePath ChildProcessHost::GetChildPath(int flags) {
67   base::FilePath child_path;
68 
69   child_path = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath(
70       switches::kBrowserSubprocessPath);
71 
72 #if defined(OS_LINUX)
73   // Use /proc/self/exe rather than our known binary path so updates
74   // can't swap out the binary from underneath us.
75   if (child_path.empty() && flags & CHILD_ALLOW_SELF)
76     child_path = base::FilePath(base::kProcSelfExe);
77 #endif
78 
79   // On most platforms, the child executable is the same as the current
80   // executable.
81   if (child_path.empty())
82     base::PathService::Get(CHILD_PROCESS_EXE, &child_path);
83 
84 #if defined(OS_MACOSX)
85   std::string child_base_name = child_path.BaseName().value();
86 
87   if (flags != CHILD_NORMAL && base::mac::AmIBundled()) {
88     // This is a specialized helper, with the |child_path| at
89     // ../Framework.framework/Versions/X/Helpers/Chromium Helper.app/Contents/
90     // MacOS/Chromium Helper. Go back up to the "Helpers" directory to select
91     // a different variant.
92     child_path = child_path.DirName().DirName().DirName().DirName();
93 
94     if (flags == CHILD_RENDERER) {
95       child_base_name += kMacHelperSuffix_renderer;
96     } else if (flags == CHILD_GPU) {
97       child_base_name += kMacHelperSuffix_gpu;
98     } else if (flags == CHILD_PLUGIN) {
99       child_base_name += kMacHelperSuffix_plugin;
100     } else {
101       NOTREACHED();
102     }
103 
104     child_path = child_path.Append(child_base_name + ".app")
105                      .Append("Contents")
106                      .Append("MacOS")
107                      .Append(child_base_name);
108   }
109 #endif
110 
111   return child_path;
112 }
113 
ChildProcessHostImpl(ChildProcessHostDelegate * delegate,IpcMode ipc_mode)114 ChildProcessHostImpl::ChildProcessHostImpl(ChildProcessHostDelegate* delegate,
115                                            IpcMode ipc_mode)
116     : ipc_mode_(ipc_mode), delegate_(delegate), opening_channel_(false) {
117   if (ipc_mode_ == IpcMode::kLegacy) {
118     // In legacy mode, we only have an IPC Channel. Bind ChildProcess to a
119     // disconnected pipe so it quietly discards messages.
120     ignore_result(child_process_.BindNewPipeAndPassReceiver());
121     channel_ = IPC::ChannelMojo::Create(
122         mojo_invitation_->AttachMessagePipe(0), IPC::Channel::MODE_SERVER, this,
123         base::ThreadTaskRunnerHandle::Get(),
124         base::ThreadTaskRunnerHandle::Get(),
125         mojo::internal::MessageQuotaChecker::MaybeCreate());
126   } else if (ipc_mode_ == IpcMode::kNormal) {
127     child_process_.Bind(mojo::PendingRemote<mojom::ChildProcess>(
128         mojo_invitation_->AttachMessagePipe(0), /*version=*/0));
129     child_process_->Initialize(bootstrap_receiver_.BindNewPipeAndPassRemote());
130   }
131 }
132 
~ChildProcessHostImpl()133 ChildProcessHostImpl::~ChildProcessHostImpl() {
134   // If a channel was never created than it wasn't registered and the filters
135   // weren't notified. For the sake of symmetry don't call the matching teardown
136   // functions. This is analogous to how RenderProcessHostImpl handles things.
137   if (!channel_)
138     return;
139 
140   for (size_t i = 0; i < filters_.size(); ++i) {
141     filters_[i]->OnChannelClosing();
142     filters_[i]->OnFilterRemoved();
143   }
144 }
145 
AddFilter(IPC::MessageFilter * filter)146 void ChildProcessHostImpl::AddFilter(IPC::MessageFilter* filter) {
147   filters_.push_back(filter);
148 
149   if (channel_)
150     filter->OnFilterAdded(channel_.get());
151 }
152 
BindReceiver(mojo::GenericPendingReceiver receiver)153 void ChildProcessHostImpl::BindReceiver(mojo::GenericPendingReceiver receiver) {
154   child_process_->BindReceiver(std::move(receiver));
155 }
156 
RunService(const std::string & service_name,mojo::PendingReceiver<service_manager::mojom::Service> receiver)157 void ChildProcessHostImpl::RunService(
158     const std::string& service_name,
159     mojo::PendingReceiver<service_manager::mojom::Service> receiver) {
160   child_process_->RunService(service_name, std::move(receiver));
161 }
162 
ForceShutdown()163 void ChildProcessHostImpl::ForceShutdown() {
164   child_process_->ProcessShutdown();
165 }
166 
167 base::Optional<mojo::OutgoingInvitation>&
GetMojoInvitation()168 ChildProcessHostImpl::GetMojoInvitation() {
169   return mojo_invitation_;
170 }
171 
CreateChannelMojo()172 void ChildProcessHostImpl::CreateChannelMojo() {
173   // If in legacy mode, |channel_| is already initialized by the constructor
174   // not bound through the ChildProcess API.
175   if (ipc_mode_ != IpcMode::kLegacy) {
176     DCHECK(!channel_);
177     DCHECK_EQ(ipc_mode_, IpcMode::kNormal);
178     DCHECK(child_process_);
179 
180     mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap;
181     auto bootstrap_receiver = bootstrap.InitWithNewPipeAndPassReceiver();
182     child_process_->BootstrapLegacyIpc(std::move(bootstrap_receiver));
183     channel_ = IPC::ChannelMojo::Create(
184         bootstrap.PassPipe(), IPC::Channel::MODE_SERVER, this,
185         base::ThreadTaskRunnerHandle::Get(),
186         base::ThreadTaskRunnerHandle::Get(),
187         mojo::internal::MessageQuotaChecker::MaybeCreate());
188   }
189   DCHECK(channel_);
190 
191   bool initialized = InitChannel();
192   DCHECK(initialized);
193 }
194 
InitChannel()195 bool ChildProcessHostImpl::InitChannel() {
196   if (!channel_->Connect())
197     return false;
198 
199   for (size_t i = 0; i < filters_.size(); ++i)
200     filters_[i]->OnFilterAdded(channel_.get());
201 
202   delegate_->OnChannelInitialized(channel_.get());
203 
204   // Make sure these messages get sent first.
205 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
206   bool enabled = IPC::Logging::GetInstance()->Enabled();
207   child_process_->SetIPCLoggingEnabled(enabled);
208 #endif
209 
210 #if BUILDFLAG(CLANG_PROFILING_INSIDE_SANDBOX)
211   child_process_->SetProfilingFile(OpenProfilingFile());
212 #endif
213 
214   opening_channel_ = true;
215 
216   return true;
217 }
218 
IsChannelOpening()219 bool ChildProcessHostImpl::IsChannelOpening() {
220   return opening_channel_;
221 }
222 
Send(IPC::Message * message)223 bool ChildProcessHostImpl::Send(IPC::Message* message) {
224   if (!channel_) {
225     delete message;
226     return false;
227   }
228   return channel_->Send(message);
229 }
230 
GenerateChildProcessUniqueId()231 int ChildProcessHostImpl::GenerateChildProcessUniqueId() {
232   // This function must be threadsafe.
233   //
234   // Historically, this function returned ids started with 1, so in several
235   // places in the code a value of 0 (rather than kInvalidUniqueID) was used as
236   // an invalid value. So we retain those semantics.
237   int id = g_unique_id.GetNext() + 1;
238 
239   CHECK_NE(0, id);
240   CHECK_NE(kInvalidUniqueID, id);
241 
242   return id;
243 }
244 
ChildProcessUniqueIdToTracingProcessId(int child_process_id)245 uint64_t ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
246     int child_process_id) {
247   // In single process mode, all the children are hosted in the same process,
248   // therefore the generated memory dump guids should not be conditioned by the
249   // child process id. The clients need not be aware of SPM and the conversion
250   // takes care of the SPM special case while translating child process ids to
251   // tracing process ids.
252   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
253           switches::kSingleProcess))
254     return memory_instrumentation::mojom::kServiceTracingProcessId;
255 
256   // The hash value is incremented so that the tracing id is never equal to
257   // MemoryDumpManager::kInvalidTracingProcessId.
258   return static_cast<uint64_t>(base::PersistentHash(
259              base::as_bytes(base::make_span(&child_process_id, 1)))) +
260          1;
261 }
262 
BindProcessHost(mojo::PendingReceiver<mojom::ChildProcessHost> receiver)263 void ChildProcessHostImpl::BindProcessHost(
264     mojo::PendingReceiver<mojom::ChildProcessHost> receiver) {
265   receiver_.Bind(std::move(receiver));
266 }
267 
BindHostReceiver(mojo::GenericPendingReceiver receiver)268 void ChildProcessHostImpl::BindHostReceiver(
269     mojo::GenericPendingReceiver receiver) {
270   delegate_->BindHostReceiver(std::move(receiver));
271 }
272 
OnMessageReceived(const IPC::Message & msg)273 bool ChildProcessHostImpl::OnMessageReceived(const IPC::Message& msg) {
274 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
275   IPC::Logging* logger = IPC::Logging::GetInstance();
276   if (msg.type() == IPC_LOGGING_ID) {
277     logger->OnReceivedLoggingMessage(msg);
278     return true;
279   }
280 
281   if (logger->Enabled())
282     logger->OnPreDispatchMessage(msg);
283 #endif
284 
285   bool handled = false;
286   for (size_t i = 0; i < filters_.size(); ++i) {
287     if (filters_[i]->OnMessageReceived(msg)) {
288       handled = true;
289       break;
290     }
291   }
292 
293   if (!handled) {
294       handled = delegate_->OnMessageReceived(msg);
295   }
296 
297 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
298   if (logger->Enabled())
299     logger->OnPostDispatchMessage(msg);
300 #endif
301   return handled;
302 }
303 
OnChannelConnected(int32_t peer_pid)304 void ChildProcessHostImpl::OnChannelConnected(int32_t peer_pid) {
305   if (!peer_process_.IsValid()) {
306     peer_process_ = base::Process::OpenWithExtraPrivileges(peer_pid);
307     if (!peer_process_.IsValid())
308        peer_process_ = delegate_->GetProcess().Duplicate();
309     DCHECK(peer_process_.IsValid());
310   }
311   opening_channel_ = false;
312   delegate_->OnChannelConnected(peer_pid);
313   for (size_t i = 0; i < filters_.size(); ++i)
314     filters_[i]->OnChannelConnected(peer_pid);
315 }
316 
OnChannelError()317 void ChildProcessHostImpl::OnChannelError() {
318   opening_channel_ = false;
319   delegate_->OnChannelError();
320 
321   for (size_t i = 0; i < filters_.size(); ++i)
322     filters_[i]->OnChannelError();
323 
324   // This will delete host_, which will also destroy this!
325   delegate_->OnChildDisconnected();
326 }
327 
OnBadMessageReceived(const IPC::Message & message)328 void ChildProcessHostImpl::OnBadMessageReceived(const IPC::Message& message) {
329   delegate_->OnBadMessageReceived(message);
330 }
331 
332 }  // namespace content
333