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