UtilityProcessHost.cpp (12868B)
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/. */ 6 7 #include "UtilityProcessHost.h" 8 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" 14 15 #include "chrome/common/process_watcher.h" 16 #include "mozilla/Preferences.h" 17 #include "mozilla/StaticPrefs_general.h" 18 19 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 20 # include "mozilla/Sandbox.h" 21 #endif 22 23 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 24 # include "mozilla/SandboxBrokerPolicyFactory.h" 25 #endif 26 27 #if defined(XP_WIN) 28 # include "mozilla/WinDllServices.h" 29 #endif // defined(XP_WIN) 30 31 #if defined(MOZ_WMF_CDM) && defined(MOZ_SANDBOX) && !defined(MOZ_ASAN) 32 # define MOZ_WMF_CDM_LPAC_SANDBOX true 33 #endif 34 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" 41 # include "nsIFile.h" 42 # include "sandboxBroker.h" 43 #endif 44 45 #include "ProfilerParent.h" 46 #include "mozilla/PProfilerChild.h" 47 48 namespace mozilla::ipc { 49 50 LazyLogModule gUtilityProcessLog("utilityproc"); 51 #define LOGD(...) MOZ_LOG(gUtilityProcessLog, LogLevel::Debug, (__VA_ARGS__)) 52 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__)) 57 #endif 58 59 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 60 bool UtilityProcessHost::sLaunchWithMacSandbox = false; 61 #endif 62 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)), 68 mLaunchPromise(MakeRefPtr<LaunchPromiseType::Private>(__func__)) { 69 MOZ_COUNT_CTOR(UtilityProcessHost); 70 LOGD("[%p] UtilityProcessHost::UtilityProcessHost sandboxingKind=%" PRIu64, 71 this, aSandbox); 72 73 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 74 if (!sLaunchWithMacSandbox) { 75 sLaunchWithMacSandbox = IsUtilitySandboxEnabled(aSandbox); 76 } 77 mDisableOSActivityMode = sLaunchWithMacSandbox; 78 #endif 79 #if defined(MOZ_SANDBOX) 80 mSandbox = aSandbox; 81 #endif 82 } 83 84 UtilityProcessHost::~UtilityProcessHost() { 85 MOZ_COUNT_DTOR(UtilityProcessHost); 86 #if defined(MOZ_SANDBOX) 87 LOGD("[%p] UtilityProcessHost::~UtilityProcessHost sandboxingKind=%" PRIu64, 88 this, mSandbox); 89 #else 90 LOGD("[%p] UtilityProcessHost::~UtilityProcessHost", this); 91 #endif 92 } 93 94 bool UtilityProcessHost::Launch(geckoargs::ChildProcessArgs aExtraOpts) { 95 MOZ_ASSERT(NS_IsMainThread()); 96 97 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Unlaunched); 98 MOZ_ASSERT(!mUtilityProcessParent); 99 100 LOGD("[%p] UtilityProcessHost::Launch", this); 101 102 mPrefSerializer = MakeUnique<ipc::SharedPreferenceSerializer>(); 103 if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Utility, 104 /* remoteType */ ""_ns)) { 105 return false; 106 } 107 mPrefSerializer->AddSharedPrefCmdLineArgs(*this, aExtraOpts); 108 109 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX 110 EnsureWidevineL1PathForSandbox(aExtraOpts); 111 #endif 112 113 mLaunchPhase = LaunchPhase::Waiting; 114 115 if (!GeckoChildProcessHost::AsyncLaunch(std::move(aExtraOpts))) { 116 NS_WARNING("UtilityProcess AsyncLaunch failed, aborting."); 117 mLaunchPhase = LaunchPhase::Complete; 118 mPrefSerializer = nullptr; 119 return false; 120 } 121 LOGD("[%p] UtilityProcessHost::Launch launching async", this); 122 return true; 123 } 124 125 RefPtr<UtilityProcessHost::LaunchPromiseType> 126 UtilityProcessHost::LaunchPromise() { 127 MOZ_ASSERT(NS_IsMainThread()); 128 129 if (mLaunchPromiseLaunched) { 130 return mLaunchPromise; 131 } 132 133 WhenProcessHandleReady()->Then( 134 GetCurrentSerialEventTarget(), __func__, 135 [this, liveToken = mLiveToken]( 136 const ipc::ProcessHandlePromise::ResolveOrRejectValue& aResult) { 137 if (!*liveToken) { 138 // The UtilityProcessHost got deleted. Abort. The promise would have 139 // already been rejected. 140 return; 141 } 142 if (mLaunchCompleted) { 143 return; 144 } 145 mLaunchCompleted = true; 146 if (aResult.IsReject()) { 147 RejectPromise(aResult.RejectValue()); 148 } 149 // If aResult.IsResolve() then we have succeeded in launching the 150 // Utility process. The promise will be resolved once the channel has 151 // connected (or failed to) later. 152 }); 153 154 mLaunchPromiseLaunched = true; 155 return mLaunchPromise; 156 } 157 158 void UtilityProcessHost::OnChannelConnected(base::ProcessId peer_pid) { 159 MOZ_ASSERT(!NS_IsMainThread()); 160 LOGD("[%p] UtilityProcessHost::OnChannelConnected", this); 161 162 GeckoChildProcessHost::OnChannelConnected(peer_pid); 163 164 NS_DispatchToMainThread(NS_NewRunnableFunction( 165 "UtilityProcessHost::OnChannelConnected", 166 [this, liveToken = mLiveToken]() { 167 if (*liveToken && mLaunchPhase == LaunchPhase::Waiting) { 168 InitAfterConnect(true); 169 } 170 })); 171 } 172 173 void UtilityProcessHost::InitAfterConnect(bool aSucceeded) { 174 MOZ_ASSERT(NS_IsMainThread()); 175 176 MOZ_ASSERT(mLaunchPhase == LaunchPhase::Waiting); 177 MOZ_ASSERT(!mUtilityProcessParent); 178 179 // This function is patterned after other `FooProcessHost` functions, but we 180 // never actually call it with `false`. 181 MOZ_ASSERT(aSucceeded); 182 183 mLaunchPhase = LaunchPhase::Complete; 184 185 mUtilityProcessParent = MakeRefPtr<UtilityProcessParent>(this); 186 DebugOnly<bool> rv = TakeInitialEndpoint().Bind(mUtilityProcessParent.get()); 187 MOZ_ASSERT(rv); 188 189 // Only clear mPrefSerializer in the success case to avoid a 190 // possible race in the case case of a timeout on Windows launch. 191 // See Bug 1555076 comment 7: 192 // https://bugzilla.mozilla.org/show_bug.cgi?id=1555076#c7 193 mPrefSerializer = nullptr; 194 195 Maybe<FileDescriptor> brokerFd; 196 197 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 198 UniquePtr<SandboxBroker::Policy> policy; 199 switch (mSandbox) { 200 case SandboxingKind::GENERIC_UTILITY: 201 policy = SandboxBrokerPolicyFactory::GetUtilityProcessPolicy( 202 GetActor()->OtherPid()); 203 break; 204 205 default: 206 MOZ_ASSERT(false, "Invalid SandboxingKind"); 207 break; 208 } 209 if (policy != nullptr) { 210 brokerFd = Some(FileDescriptor()); 211 mSandboxBroker = SandboxBroker::Create( 212 std::move(policy), GetActor()->OtherPid(), brokerFd.ref()); 213 // This is unlikely to fail and probably indicates OS resource 214 // exhaustion, but we can at least try to recover. 215 (void)NS_WARN_IF(mSandboxBroker == nullptr); 216 MOZ_ASSERT(brokerFd.ref().IsValid()); 217 } 218 #endif // XP_LINUX && MOZ_SANDBOX 219 220 bool isReadyForBackgroundProcessing = false; 221 #if defined(XP_WIN) 222 RefPtr<DllServices> dllSvc(DllServices::Get()); 223 isReadyForBackgroundProcessing = dllSvc->IsReadyForBackgroundProcessing(); 224 #endif 225 226 (void)GetActor()->SendInit(brokerFd, Telemetry::CanRecordReleaseData(), 227 isReadyForBackgroundProcessing); 228 229 (void)GetActor()->SendInitProfiler( 230 ProfilerParent::CreateForProcess(GetActor()->OtherPid())); 231 232 LOGD("[%p] UtilityProcessHost::InitAfterConnect succeeded", this); 233 234 // Promise will be resolved later, from UtilityProcessParent when the child 235 // will send the InitCompleted message. 236 } 237 238 void UtilityProcessHost::Shutdown() { 239 MOZ_ASSERT(NS_IsMainThread()); 240 MOZ_ASSERT(!mShutdownRequested); 241 LOGD("[%p] UtilityProcessHost::Shutdown", this); 242 243 RejectPromise(LaunchError("aborted by UtilityProcessHost::Shutdown")); 244 245 if (mUtilityProcessParent) { 246 LOGD("[%p] UtilityProcessHost::Shutdown not destroying utility process.", 247 this); 248 249 // OnChannelClosed uses this to check if the shutdown was expected or 250 // unexpected. 251 mShutdownRequested = true; 252 253 // The channel might already be closed if we got here unexpectedly. 254 if (mUtilityProcessParent->CanSend()) { 255 mUtilityProcessParent->Close(); 256 } 257 258 #ifndef NS_FREE_PERMANENT_DATA 259 // No need to communicate shutdown, the Utility process doesn't need to 260 // communicate anything back. 261 KillHard("NormalShutdown"); 262 #endif 263 264 // If we're shutting down unexpectedly, we're in the middle of handling an 265 // ActorDestroy for PUtilityProcessParent, which is still on the stack. 266 // We'll return back to OnChannelClosed. 267 // 268 // Otherwise, we'll wait for OnChannelClose to be called whenever 269 // PUtilityProcessParent acknowledges shutdown. 270 return; 271 } 272 273 DestroyProcess(); 274 } 275 276 void UtilityProcessHost::OnChannelClosed( 277 IProtocol::ActorDestroyReason aReason) { 278 MOZ_ASSERT(NS_IsMainThread()); 279 LOGD("[%p] UtilityProcessHost::OnChannelClosed", this); 280 281 // `aReason` was not originally passed into this function; a value of 0 for 282 // the `why` in telemetry means it's from an older build with no info 283 RejectPromise( 284 LaunchError("UtilityProcessHost::OnChannelClosed", 1 + (long)aReason)); 285 286 if (!mShutdownRequested && mListener) { 287 // This is an unclean shutdown. Notify our listener that we're going away. 288 mListener->OnProcessUnexpectedShutdown(this); 289 } 290 291 DestroyProcess(); 292 293 // Release the actor. 294 UtilityProcessParent::Destroy(std::move(mUtilityProcessParent)); 295 } 296 297 void UtilityProcessHost::KillHard(const char* aReason) { 298 MOZ_ASSERT(NS_IsMainThread()); 299 LOGD("[%p] UtilityProcessHost::KillHard", this); 300 301 ProcessHandle handle = GetChildProcessHandle(); 302 if (!base::KillProcess(handle, base::PROCESS_END_KILLED_BY_USER)) { 303 NS_WARNING("failed to kill subprocess!"); 304 } 305 306 SetAlreadyDead(); 307 } 308 309 void UtilityProcessHost::DestroyProcess() { 310 MOZ_ASSERT(NS_IsMainThread()); 311 LOGD("[%p] UtilityProcessHost::DestroyProcess", this); 312 313 RejectPromise(LaunchError("UtilityProcessHost::DestroyProcess")); 314 315 // Any pending tasks will be cancelled from now on. 316 *mLiveToken = false; 317 318 NS_DispatchToMainThread( 319 NS_NewRunnableFunction("DestroyProcessRunnable", [this] { Destroy(); })); 320 } 321 322 void UtilityProcessHost::ResolvePromise() { 323 MOZ_ASSERT(NS_IsMainThread()); 324 LOGD("[%p] UtilityProcessHost connected - resolving launch promise", this); 325 326 if (!mLaunchPromiseSettled) { 327 mLaunchPromise->Resolve(Ok{}, __func__); 328 mLaunchPromiseSettled = true; 329 } 330 331 mLaunchCompleted = true; 332 } 333 334 void UtilityProcessHost::RejectPromise(LaunchError err) { 335 MOZ_ASSERT(NS_IsMainThread()); 336 LOGD("[%p] UtilityProcessHost connection failed - rejecting launch promise", 337 this); 338 339 if (!mLaunchPromiseSettled) { 340 mLaunchPromise->Reject(std::move(err), __func__); 341 mLaunchPromiseSettled = true; 342 } 343 344 mLaunchCompleted = true; 345 } 346 347 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 348 bool UtilityProcessHost::FillMacSandboxInfo(MacSandboxInfo& aInfo) { 349 GeckoChildProcessHost::FillMacSandboxInfo(aInfo); 350 if (!aInfo.shouldLog && PR_GetEnv("MOZ_SANDBOX_UTILITY_LOGGING")) { 351 aInfo.shouldLog = true; 352 } 353 return true; 354 } 355 356 /* static */ 357 MacSandboxType UtilityProcessHost::GetMacSandboxType() { 358 return MacSandboxType_Utility; 359 } 360 #endif 361 362 #ifdef MOZ_WMF_CDM_LPAC_SANDBOX 363 void UtilityProcessHost::EnsureWidevineL1PathForSandbox( 364 geckoargs::ChildProcessArgs& aExtraOpts) { 365 if (mSandbox != SandboxingKind::MF_MEDIA_ENGINE_CDM) { 366 return; 367 } 368 369 RefPtr<mozilla::gmp::GeckoMediaPluginServiceParent> gmps = 370 mozilla::gmp::GeckoMediaPluginServiceParent::GetSingleton(); 371 if (NS_WARN_IF(!gmps)) { 372 WMF_LOG("Failed to get GeckoMediaPluginServiceParent!"); 373 return; 374 } 375 376 if (!StaticPrefs::media_eme_widevine_experiment_enabled()) { 377 return; 378 } 379 380 // If Widevine L1 is installed after the MFCDM process starts, we will set it 381 // path later via MFCDMService::UpdateWideivineL1Path(). 382 nsString widevineL1Path; 383 nsCOMPtr<nsIFile> pluginFile; 384 if (NS_WARN_IF(NS_FAILED(gmps->FindPluginDirectoryForAPI( 385 nsCString(kWidevineExperimentAPIName), 386 {nsCString(kWidevineExperimentKeySystemName)}, 387 getter_AddRefs(pluginFile))))) { 388 WMF_LOG("Widevine L1 is not installed yet"); 389 return; 390 } 391 392 if (!pluginFile) { 393 WMF_LOG("No plugin file found!"); 394 return; 395 } 396 397 if (NS_WARN_IF(NS_FAILED(pluginFile->GetTarget(widevineL1Path)))) { 398 WMF_LOG("Failed to get L1 path!"); 399 return; 400 } 401 402 WMF_LOG("Set Widevine L1 path=%s", 403 NS_ConvertUTF16toUTF8(widevineL1Path).get()); 404 geckoargs::sPluginPath.Put(NS_ConvertUTF16toUTF8(widevineL1Path).get(), 405 aExtraOpts); 406 SandboxBroker::EnsureLpacPermsissionsOnDir(widevineL1Path); 407 } 408 409 # undef WMF_LOG 410 411 #endif 412 413 } // namespace mozilla::ipc