1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "UtilityProcessHost.h"
9 #include "mozilla/dom/ContentParent.h"
10 #include "mozilla/ipc/Endpoint.h"
11 #include "mozilla/ipc/UtilityProcessManager.h"
12 #include "mozilla/ipc/UtilityProcessSandboxing.h"
13 #include "mozilla/Telemetry.h"
15 #include "chrome/common/process_watcher.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/StaticPrefs_general.h"
19 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
20 # include "mozilla/Sandbox.h"
23 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
24 # include "mozilla/SandboxBrokerPolicyFactory.h"
28 # include "mozilla/WinDllServices.h"
29 #endif // defined(XP_WIN)
31 #if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX) && !defined(MOZ_ASAN)
32 # define MOZ_WMF_CDM_LPAC_SANDBOX true
35 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
36 # include "GMPServiceParent.h"
37 # include "mozilla/dom/KeySystemNames.h"
38 # include "mozilla/GeckoArgs.h"
39 # include "mozilla/MFMediaEngineUtils.h"
40 # include "mozilla/StaticPrefs_media.h"
42 # include "sandboxBroker.h"
45 #include "ProfilerParent.h"
46 #include "mozilla/PProfilerChild.h"
48 namespace mozilla::ipc
{
50 LazyLogModule
gUtilityProcessLog("utilityproc");
51 #define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__))
53 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
54 # define WMF_LOG(msg, ...) \
55 MOZ_LOG(gMFMediaEngineLog, LogLevel::Debug, \
56 ("UtilityProcessHost=%p, " msg, this, ##__VA_ARGS__))
59 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
60 bool UtilityProcessHost::sLaunchWithMacSandbox
= false;
63 UtilityProcessHost::UtilityProcessHost(SandboxingKind aSandbox
,
64 RefPtr
<Listener
> aListener
)
65 : GeckoChildProcessHost(GeckoProcessType_Utility
),
66 mListener(std::move(aListener
)),
67 mLiveToken(new media::Refcountable
<bool>(true)),
69 MakeRefPtr
<GenericNonExclusivePromise::Private
>(__func__
)) {
70 MOZ_COUNT_CTOR(UtilityProcessHost
);
71 LOGD("[%p] UtilityProcessHost::UtilityProcessHost sandboxingKind=%" PRIu64
,
74 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
75 if (!sLaunchWithMacSandbox
) {
76 sLaunchWithMacSandbox
= IsUtilitySandboxEnabled(aSandbox
);
78 mDisableOSActivityMode
= sLaunchWithMacSandbox
;
80 #if defined(MOZ_SANDBOX)
85 UtilityProcessHost::~UtilityProcessHost() {
86 MOZ_COUNT_DTOR(UtilityProcessHost
);
87 #if defined(MOZ_SANDBOX)
88 LOGD("[%p] UtilityProcessHost::~UtilityProcessHost sandboxingKind=%" PRIu64
,
91 LOGD("[%p] UtilityProcessHost::~UtilityProcessHost", this);
95 bool UtilityProcessHost::Launch(StringVector aExtraOpts
) {
96 MOZ_ASSERT(NS_IsMainThread());
98 MOZ_ASSERT(mLaunchPhase
== LaunchPhase::Unlaunched
);
99 MOZ_ASSERT(!mUtilityProcessParent
);
101 LOGD("[%p] UtilityProcessHost::Launch", this);
103 mPrefSerializer
= MakeUnique
<ipc::SharedPreferenceSerializer
>();
104 if (!mPrefSerializer
->SerializeToSharedMemory(GeckoProcessType_Utility
,
105 /* remoteType */ ""_ns
)) {
108 mPrefSerializer
->AddSharedPrefCmdLineArgs(*this, aExtraOpts
);
110 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
111 EnsureWidevineL1PathForSandbox(aExtraOpts
);
114 #if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
115 EnanbleMFCDMTelemetryEventIfNeeded();
118 mLaunchPhase
= LaunchPhase::Waiting
;
120 if (!GeckoChildProcessHost::AsyncLaunch(aExtraOpts
)) {
121 NS_WARNING("UtilityProcess AsyncLaunch failed, aborting.");
122 mLaunchPhase
= LaunchPhase::Complete
;
123 mPrefSerializer
= nullptr;
126 LOGD("[%p] UtilityProcessHost::Launch launching async", this);
130 RefPtr
<GenericNonExclusivePromise
> UtilityProcessHost::LaunchPromise() {
131 MOZ_ASSERT(NS_IsMainThread());
133 if (mLaunchPromiseLaunched
) {
134 return mLaunchPromise
;
137 WhenProcessHandleReady()->Then(
138 GetCurrentSerialEventTarget(), __func__
,
139 [this, liveToken
= mLiveToken
](
140 const ipc::ProcessHandlePromise::ResolveOrRejectValue
& aResult
) {
142 // The UtilityProcessHost got deleted. Abort. The promise would have
143 // already been rejected.
146 if (mLaunchCompleted
) {
149 mLaunchCompleted
= true;
150 if (aResult
.IsReject()) {
153 // If aResult.IsResolve() then we have succeeded in launching the
154 // Utility process. The promise will be resolved once the channel has
155 // connected (or failed to) later.
158 mLaunchPromiseLaunched
= true;
159 return mLaunchPromise
;
162 void UtilityProcessHost::OnChannelConnected(base::ProcessId peer_pid
) {
163 MOZ_ASSERT(!NS_IsMainThread());
164 LOGD("[%p] UtilityProcessHost::OnChannelConnected", this);
166 GeckoChildProcessHost::OnChannelConnected(peer_pid
);
168 NS_DispatchToMainThread(NS_NewRunnableFunction(
169 "UtilityProcessHost::OnChannelConnected",
170 [this, liveToken
= mLiveToken
]() {
171 if (*liveToken
&& mLaunchPhase
== LaunchPhase::Waiting
) {
172 InitAfterConnect(true);
177 void UtilityProcessHost::InitAfterConnect(bool aSucceeded
) {
178 MOZ_ASSERT(NS_IsMainThread());
180 MOZ_ASSERT(mLaunchPhase
== LaunchPhase::Waiting
);
181 MOZ_ASSERT(!mUtilityProcessParent
);
183 mLaunchPhase
= LaunchPhase::Complete
;
190 mUtilityProcessParent
= MakeRefPtr
<UtilityProcessParent
>(this);
191 DebugOnly
<bool> rv
= TakeInitialEndpoint().Bind(mUtilityProcessParent
.get());
194 // Only clear mPrefSerializer in the success case to avoid a
195 // possible race in the case case of a timeout on Windows launch.
196 // See Bug 1555076 comment 7:
197 // https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7
198 mPrefSerializer
= nullptr;
200 Maybe
<FileDescriptor
> brokerFd
;
202 #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
203 UniquePtr
<SandboxBroker::Policy
> policy
;
205 case SandboxingKind::GENERIC_UTILITY
:
206 policy
= SandboxBrokerPolicyFactory::GetUtilityProcessPolicy(
207 GetActor()->OtherPid());
211 MOZ_ASSERT(false, "Invalid SandboxingKind");
214 if (policy
!= nullptr) {
215 brokerFd
= Some(FileDescriptor());
216 mSandboxBroker
= SandboxBroker::Create(
217 std::move(policy
), GetActor()->OtherPid(), brokerFd
.ref());
218 // This is unlikely to fail and probably indicates OS resource
219 // exhaustion, but we can at least try to recover.
220 Unused
<< NS_WARN_IF(mSandboxBroker
== nullptr);
221 MOZ_ASSERT(brokerFd
.ref().IsValid());
223 #endif // XP_LINUX && MOZ_SANDBOX
225 bool isReadyForBackgroundProcessing
= false;
227 RefPtr
<DllServices
> dllSvc(DllServices::Get());
228 isReadyForBackgroundProcessing
= dllSvc
->IsReadyForBackgroundProcessing();
231 Unused
<< GetActor()->SendInit(brokerFd
, Telemetry::CanRecordReleaseData(),
232 isReadyForBackgroundProcessing
);
234 Unused
<< GetActor()->SendInitProfiler(
235 ProfilerParent::CreateForProcess(GetActor()->OtherPid()));
237 LOGD("[%p] UtilityProcessHost::InitAfterConnect succeeded", this);
239 // Promise will be resolved later, from UtilityProcessParent when the child
240 // will send the InitCompleted message.
243 void UtilityProcessHost::Shutdown() {
244 MOZ_ASSERT(NS_IsMainThread());
245 MOZ_ASSERT(!mShutdownRequested
);
246 LOGD("[%p] UtilityProcessHost::Shutdown", this);
250 if (mUtilityProcessParent
) {
251 LOGD("[%p] UtilityProcessHost::Shutdown not destroying utility process.",
254 // OnChannelClosed uses this to check if the shutdown was expected or
256 mShutdownRequested
= true;
258 // The channel might already be closed if we got here unexpectedly.
259 if (mUtilityProcessParent
->CanSend()) {
260 mUtilityProcessParent
->Close();
263 #ifndef NS_FREE_PERMANENT_DATA
264 // No need to communicate shutdown, the Utility process doesn't need to
265 // communicate anything back.
266 KillHard("NormalShutdown");
269 // If we're shutting down unexpectedly, we're in the middle of handling an
270 // ActorDestroy for PUtilityProcessParent, which is still on the stack.
271 // We'll return back to OnChannelClosed.
273 // Otherwise, we'll wait for OnChannelClose to be called whenever
274 // PUtilityProcessParent acknowledges shutdown.
281 void UtilityProcessHost::OnChannelClosed() {
282 MOZ_ASSERT(NS_IsMainThread());
283 LOGD("[%p] UtilityProcessHost::OnChannelClosed", this);
287 if (!mShutdownRequested
&& mListener
) {
288 // This is an unclean shutdown. Notify our listener that we're going away.
289 mListener
->OnProcessUnexpectedShutdown(this);
294 // Release the actor.
295 UtilityProcessParent::Destroy(std::move(mUtilityProcessParent
));
298 void UtilityProcessHost::KillHard(const char* aReason
) {
299 MOZ_ASSERT(NS_IsMainThread());
300 LOGD("[%p] UtilityProcessHost::KillHard", this);
302 ProcessHandle handle
= GetChildProcessHandle();
303 if (!base::KillProcess(handle
, base::PROCESS_END_KILLED_BY_USER
)) {
304 NS_WARNING("failed to kill subprocess!");
310 void UtilityProcessHost::DestroyProcess() {
311 MOZ_ASSERT(NS_IsMainThread());
312 LOGD("[%p] UtilityProcessHost::DestroyProcess", this);
316 // Any pending tasks will be cancelled from now on.
319 NS_DispatchToMainThread(
320 NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); }));
323 void UtilityProcessHost::ResolvePromise() {
324 MOZ_ASSERT(NS_IsMainThread());
325 LOGD("[%p] UtilityProcessHost connected - resolving launch promise", this);
327 if (!mLaunchPromiseSettled
) {
328 mLaunchPromise
->Resolve(true, __func__
);
329 mLaunchPromiseSettled
= true;
332 mLaunchCompleted
= true;
335 void UtilityProcessHost::RejectPromise() {
336 MOZ_ASSERT(NS_IsMainThread());
337 LOGD("[%p] UtilityProcessHost connection failed - rejecting launch promise",
340 if (!mLaunchPromiseSettled
) {
341 mLaunchPromise
->Reject(NS_ERROR_FAILURE
, __func__
);
342 mLaunchPromiseSettled
= true;
345 mLaunchCompleted
= true;
348 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
349 bool UtilityProcessHost::FillMacSandboxInfo(MacSandboxInfo
& aInfo
) {
350 GeckoChildProcessHost::FillMacSandboxInfo(aInfo
);
351 if (!aInfo
.shouldLog
&& PR_GetEnv("MOZ_SANDBOX_UTILITY_LOGGING")) {
352 aInfo
.shouldLog
= true;
358 MacSandboxType
UtilityProcessHost::GetMacSandboxType() {
359 return MacSandboxType_Utility
;
363 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX
364 void UtilityProcessHost::EnsureWidevineL1PathForSandbox(
365 StringVector
& aExtraOpts
) {
366 if (mSandbox
!= SandboxingKind::MF_MEDIA_ENGINE_CDM
) {
370 RefPtr
<mozilla::gmp::GeckoMediaPluginServiceParent
> gmps
=
371 mozilla::gmp::GeckoMediaPluginServiceParent::GetSingleton();
372 if (NS_WARN_IF(!gmps
)) {
373 WMF_LOG("Failed to get GeckoMediaPluginServiceParent!");
377 if (!StaticPrefs::media_eme_widevine_experiment_enabled()) {
381 // If Widevine L1 is installed after the MFCDM process starts, we will set it
382 // path later via MFCDMService::UpdateWideivineL1Path().
383 nsString widevineL1Path
;
384 nsCOMPtr
<nsIFile
> pluginFile
;
385 if (NS_WARN_IF(NS_FAILED(gmps
->FindPluginDirectoryForAPI(
386 nsCString(kWidevineExperimentAPIName
),
387 {nsCString(kWidevineExperimentKeySystemName
)},
388 getter_AddRefs(pluginFile
))))) {
389 WMF_LOG("Widevine L1 is not installed yet");
394 WMF_LOG("No plugin file found!");
398 if (NS_WARN_IF(NS_FAILED(pluginFile
->GetTarget(widevineL1Path
)))) {
399 WMF_LOG("Failed to get L1 path!");
403 WMF_LOG("Set Widevine L1 path=%s",
404 NS_ConvertUTF16toUTF8(widevineL1Path
).get());
405 geckoargs::sPluginPath
.Put(NS_ConvertUTF16toUTF8(widevineL1Path
).get(),
407 SandboxBroker::EnsureLpacPermsissionsOnDir(widevineL1Path
);
414 #if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX)
415 void UtilityProcessHost::EnanbleMFCDMTelemetryEventIfNeeded() const {
416 if (mSandbox
!= SandboxingKind::MF_MEDIA_ENGINE_CDM
) {
419 static bool sTelemetryEventEnabled
= false;
420 if (!sTelemetryEventEnabled
) {
421 sTelemetryEventEnabled
= true;
422 Telemetry::SetEventRecordingEnabled("mfcdm"_ns
, true);
427 } // namespace mozilla::ipc