UtilityProcessChild.cpp (13486B)
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 #include "UtilityProcessChild.h" 7 8 #include "mozilla/AppShutdown.h" 9 #include "mozilla/Logging.h" 10 #include "mozilla/dom/ContentParent.h" 11 #include "mozilla/dom/JSOracleChild.h" 12 #include "mozilla/dom/MemoryReportRequest.h" 13 #include "mozilla/ipc/CrashReporterClient.h" 14 #include "mozilla/ipc/Endpoint.h" 15 #include "mozilla/ipc/UtilityProcessManager.h" 16 #include "mozilla/ipc/UtilityProcessSandboxing.h" 17 #include "mozilla/Preferences.h" 18 #include "mozilla/RemoteMediaManagerParent.h" 19 20 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 21 # include "mozilla/Sandbox.h" 22 # include "mozilla/SandboxProfilerObserver.h" 23 #endif 24 25 #if defined(XP_OPENBSD) && defined(MOZ_SANDBOX) 26 # include "mozilla/SandboxSettings.h" 27 #endif 28 29 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) 30 # include "mozilla/SandboxTestingChild.h" 31 #endif 32 33 #include "mozilla/Telemetry.h" 34 35 #if defined(XP_WIN) 36 # include "mozilla/WinDllServices.h" 37 # include "mozilla/dom/WindowsUtilsChild.h" 38 # include "mozilla/widget/filedialog/WinFileDialogChild.h" 39 #endif 40 41 #include "nsDebugImpl.h" 42 #include "nsIXULRuntime.h" 43 #include "nsThreadManager.h" 44 #include "GeckoProfiler.h" 45 46 #include "mozilla/ipc/ProcessChild.h" 47 #include "mozilla/FOGIPC.h" 48 #include "mozilla/glean/GleanTestsTestMetrics.h" 49 50 #include "mozilla/Services.h" 51 52 namespace TelemetryScalar { 53 void Set(mozilla::Telemetry::ScalarID aId, uint32_t aValue); 54 } 55 56 namespace mozilla::ipc { 57 58 using namespace layers; 59 60 static StaticMutex sUtilityProcessChildMutex; 61 static StaticRefPtr<UtilityProcessChild> sUtilityProcessChild 62 MOZ_GUARDED_BY(sUtilityProcessChildMutex); 63 64 UtilityProcessChild::UtilityProcessChild() : mChildStartTime(TimeStamp::Now()) { 65 nsDebugImpl::SetMultiprocessMode("Utility"); 66 } 67 68 UtilityProcessChild::~UtilityProcessChild() = default; 69 70 /* static */ 71 RefPtr<UtilityProcessChild> UtilityProcessChild::GetSingleton() { 72 MOZ_ASSERT(XRE_IsUtilityProcess()); 73 if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) { 74 return nullptr; 75 } 76 StaticMutexAutoLock lock(sUtilityProcessChildMutex); 77 if (!sUtilityProcessChild) { 78 sUtilityProcessChild = new UtilityProcessChild(); 79 } 80 return sUtilityProcessChild; 81 } 82 83 /* static */ 84 RefPtr<UtilityProcessChild> UtilityProcessChild::Get() { 85 StaticMutexAutoLock lock(sUtilityProcessChildMutex); 86 return sUtilityProcessChild; 87 } 88 89 bool UtilityProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint, 90 const nsCString& aParentBuildID, 91 uint64_t aSandboxingKind) { 92 MOZ_ASSERT(NS_IsMainThread()); 93 94 // Initialize the thread manager before starting IPC. Otherwise, messages 95 // may be posted to the main thread and we won't be able to process them. 96 if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) { 97 return false; 98 } 99 100 // Now it's safe to start IPC. 101 if (NS_WARN_IF(!aEndpoint.Bind(this))) { 102 return false; 103 } 104 105 // This must be checked before any IPDL message, which may hit sentinel 106 // errors due to parent and content processes having different 107 // versions. 108 MessageChannel* channel = GetIPCChannel(); 109 if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID.get())) { 110 // We need to quit this process if the buildID doesn't match the parent's. 111 // This can occur when an update occurred in the background. 112 ipc::ProcessChild::QuickExit(); 113 } 114 115 // Init crash reporter support. 116 ipc::CrashReporterClient::InitSingleton(this); 117 118 if (NS_FAILED(NS_InitMinimalXPCOM())) { 119 return false; 120 } 121 122 mSandbox = (SandboxingKind)aSandboxingKind; 123 124 // At the moment, only ORB uses JSContext in the 125 // Utility Process and ORB uses GENERIC_UTILITY 126 if (mSandbox == SandboxingKind::GENERIC_UTILITY) { 127 if (!JS_FrontendOnlyInit()) { 128 return false; 129 } 130 #if defined(__OpenBSD__) && defined(MOZ_SANDBOX) 131 // Bug 1823458: delay pledge initialization, otherwise 132 // JS_FrontendOnlyInit triggers sysctl(KERN_PROC_ID) which isnt 133 // permitted with the current pledge.utility config 134 StartOpenBSDSandbox(GeckoProcessType_Utility, mSandbox); 135 #endif 136 } 137 138 profiler_set_process_name(nsCString("Utility Process")); 139 140 // Notify the parent process that we have finished our init and that it can 141 // now resolve the pending promise of process startup 142 SendInitCompleted(); 143 144 PROFILER_MARKER_UNTYPED( 145 "UtilityProcessChild::SendInitCompleted", IPC, 146 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime))); 147 148 RunOnShutdown( 149 [sandboxKind = mSandbox] { 150 StaticMutexAutoLock lock(sUtilityProcessChildMutex); 151 sUtilityProcessChild = nullptr; 152 if (sandboxKind == SandboxingKind::GENERIC_UTILITY) { 153 JS_FrontendOnlyShutDown(); 154 } 155 }, 156 ShutdownPhase::XPCOMShutdownFinal); 157 158 return true; 159 } 160 161 #if defined(XP_MACOSX) && defined(MOZ_SANDBOX) 162 extern "C" { 163 void CGSShutdownServerConnections(); 164 }; 165 #endif 166 167 mozilla::ipc::IPCResult UtilityProcessChild::RecvInit( 168 const Maybe<FileDescriptor>& aBrokerFd, 169 const bool& aCanRecordReleaseTelemetry, 170 const bool& aIsReadyForBackgroundProcessing) { 171 // Do this now (before closing WindowServer on macOS) to avoid risking 172 // blocking in GetCurrentProcess() called on that platform 173 mozilla::ipc::SetThisProcessName("Utility Process"); 174 175 #if defined(MOZ_SANDBOX) 176 # if defined(XP_MACOSX) 177 // Close all current connections to the WindowServer. This ensures that the 178 // Activity Monitor will not label the content process as "Not responding" 179 // because it's not running a native event loop. See bug 1384336. 180 CGSShutdownServerConnections(); 181 182 # elif defined(XP_LINUX) 183 int fd = -1; 184 if (aBrokerFd.isSome()) { 185 fd = aBrokerFd.value().ClonePlatformHandle().release(); 186 } 187 188 RegisterProfilerObserversForSandboxProfiler(); 189 SetUtilitySandbox(fd, mSandbox); 190 191 # endif // XP_MACOSX/XP_LINUX 192 #endif // MOZ_SANDBOX 193 194 #if defined(XP_WIN) 195 if (aCanRecordReleaseTelemetry) { 196 RefPtr<DllServices> dllSvc(DllServices::Get()); 197 dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing); 198 } 199 #endif // defined(XP_WIN) 200 201 PROFILER_MARKER_UNTYPED( 202 "UtilityProcessChild::RecvInit", IPC, 203 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime))); 204 return IPC_OK(); 205 } 206 207 mozilla::ipc::IPCResult UtilityProcessChild::RecvPreferenceUpdate( 208 const Pref& aPref) { 209 Preferences::SetPreference(aPref); 210 return IPC_OK(); 211 } 212 213 mozilla::ipc::IPCResult UtilityProcessChild::RecvInitProfiler( 214 Endpoint<PProfilerChild>&& aEndpoint) { 215 mProfilerController = ChildProfilerController::Create(std::move(aEndpoint)); 216 return IPC_OK(); 217 } 218 219 mozilla::ipc::IPCResult UtilityProcessChild::RecvRequestMemoryReport( 220 const uint32_t& aGeneration, const bool& aAnonymize, 221 const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile, 222 const RequestMemoryReportResolver& aResolver) { 223 nsPrintfCString processName("Utility (pid %" PRIPID 224 ", sandboxingKind %" PRIu64 ")", 225 base::GetCurrentProcId(), mSandbox); 226 227 mozilla::dom::MemoryReportRequestClient::Start( 228 aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName, 229 [&](const MemoryReport& aReport) { 230 (void)GetSingleton()->SendAddMemoryReport(aReport); 231 }, 232 aResolver); 233 return IPC_OK(); 234 } 235 236 #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) 237 mozilla::ipc::IPCResult UtilityProcessChild::RecvInitSandboxTesting( 238 Endpoint<PSandboxTestingChild>&& aEndpoint) { 239 if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) { 240 return IPC_FAIL( 241 this, "InitSandboxTesting failed to initialise the child process."); 242 } 243 return IPC_OK(); 244 } 245 #endif 246 247 mozilla::ipc::IPCResult UtilityProcessChild::RecvFlushFOGData( 248 FlushFOGDataResolver&& aResolver) { 249 glean::FlushFOGData(std::move(aResolver)); 250 return IPC_OK(); 251 } 252 253 mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTriggerMetrics( 254 TestTriggerMetricsResolver&& aResolve) { 255 mozilla::glean::test_only_ipc::a_counter.Add( 256 nsIXULRuntime::PROCESS_TYPE_UTILITY); 257 aResolve(true); 258 return IPC_OK(); 259 } 260 261 mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTelemetryProbes() { 262 const uint32_t kExpectedUintValue = 42; 263 TelemetryScalar::Set(Telemetry::ScalarID::TELEMETRY_TEST_UTILITY_ONLY_UINT, 264 kExpectedUintValue); 265 return IPC_OK(); 266 } 267 268 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartUtilityMediaService( 269 Endpoint<PUtilityMediaServiceParent>&& aEndpoint, 270 nsTArray<gfx::GfxVarUpdate>&& aUpdates) { 271 PROFILER_MARKER_UNTYPED( 272 "UtilityProcessChild::RecvStartUtilityMediaService", MEDIA, 273 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime))); 274 mUtilityMediaServiceInstance = 275 new UtilityMediaServiceParent(std::move(aUpdates)); 276 if (!mUtilityMediaServiceInstance) { 277 return IPC_FAIL(this, "Failed to create UtilityMediaServiceParent"); 278 } 279 280 mUtilityMediaServiceInstance->Start(std::move(aEndpoint)); 281 return IPC_OK(); 282 } 283 284 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartJSOracleService( 285 Endpoint<PJSOracleChild>&& aEndpoint) { 286 PROFILER_MARKER_UNTYPED( 287 "UtilityProcessChild::RecvStartJSOracleService", JS, 288 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime))); 289 mJSOracleInstance = new mozilla::dom::JSOracleChild(); 290 if (!mJSOracleInstance) { 291 return IPC_FAIL(this, "Failed to create JSOracleParent"); 292 } 293 294 mJSOracleInstance->Start(std::move(aEndpoint)); 295 return IPC_OK(); 296 } 297 298 #if defined(XP_WIN) 299 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWindowsUtilsService( 300 Endpoint<dom::PWindowsUtilsChild>&& aEndpoint) { 301 PROFILER_MARKER_UNTYPED( 302 "UtilityProcessChild::RecvStartWindowsUtilsService", OTHER, 303 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime))); 304 mWindowsUtilsInstance = new dom::WindowsUtilsChild(); 305 if (!mWindowsUtilsInstance) { 306 return IPC_FAIL(this, "Failed to create WindowsUtilsChild"); 307 } 308 309 [[maybe_unused]] bool ok = std::move(aEndpoint).Bind(mWindowsUtilsInstance); 310 MOZ_ASSERT(ok); 311 return IPC_OK(); 312 } 313 314 mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWinFileDialogService( 315 Endpoint<widget::filedialog::PWinFileDialogChild>&& aEndpoint) { 316 PROFILER_MARKER_UNTYPED( 317 "UtilityProcessChild::RecvStartWinFileDialogService", OTHER, 318 MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime))); 319 320 auto instance = MakeRefPtr<widget::filedialog::WinFileDialogChild>(); 321 if (!instance) { 322 return IPC_FAIL(this, "Failed to create WinFileDialogChild"); 323 } 324 325 bool const ok = std::move(aEndpoint).Bind(instance.get()); 326 if (!ok) { 327 return IPC_FAIL(this, "Failed to bind created WinFileDialogChild"); 328 } 329 330 return IPC_OK(); 331 } 332 333 mozilla::ipc::IPCResult UtilityProcessChild::RecvGetUntrustedModulesData( 334 GetUntrustedModulesDataResolver&& aResolver) { 335 RefPtr<DllServices> dllSvc(DllServices::Get()); 336 dllSvc->GetUntrustedModulesData()->Then( 337 GetMainThreadSerialEventTarget(), __func__, 338 [aResolver](Maybe<UntrustedModulesData>&& aData) { 339 aResolver(std::move(aData)); 340 }, 341 [aResolver](nsresult aReason) { aResolver(Nothing()); }); 342 return IPC_OK(); 343 } 344 345 mozilla::ipc::IPCResult 346 UtilityProcessChild::RecvUnblockUntrustedModulesThread() { 347 if (nsCOMPtr<nsIObserverService> obs = 348 mozilla::services::GetObserverService()) { 349 obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr); 350 } 351 return IPC_OK(); 352 } 353 #endif // defined(XP_WIN) 354 355 void UtilityProcessChild::ActorDestroy(ActorDestroyReason aWhy) { 356 #if defined(XP_LINUX) && defined(MOZ_SANDBOX) 357 DestroySandboxProfiler(); 358 #endif 359 360 if (AbnormalShutdown == aWhy) { 361 NS_WARNING("Shutting down Utility process early due to a crash!"); 362 ipc::ProcessChild::QuickExit(); 363 } 364 365 // Send the last bits of Glean data over to the main process. 366 glean::FlushFOGData( 367 [](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); }); 368 369 #ifndef NS_FREE_PERMANENT_DATA 370 ProcessChild::QuickExit(); 371 #else 372 373 if (mProfilerController) { 374 mProfilerController->Shutdown(); 375 mProfilerController = nullptr; 376 } 377 378 uint32_t timeout = 0; 379 if (mUtilityMediaServiceInstance) { 380 mUtilityMediaServiceInstance = nullptr; 381 timeout = 10 * 1000; 382 } 383 384 mJSOracleInstance = nullptr; 385 386 # ifdef XP_WIN 387 mWindowsUtilsInstance = nullptr; 388 # endif 389 390 // Wait until all RemoteMediaManagerParent have closed. 391 // It is still possible some may not have clean up yet, and we might hit 392 // timeout. Our xpcom-shutdown listener should take care of cleaning the 393 // reference of our singleton. 394 // 395 // FIXME: Should move from using AsyncBlockers to proper 396 // nsIAsyncShutdownService once it is not JS, see bug 1760855 397 mShutdownBlockers.WaitUntilClear(timeout)->Then( 398 GetCurrentSerialEventTarget(), __func__, [&]() { 399 # ifdef XP_WIN 400 { 401 RefPtr<DllServices> dllSvc(DllServices::Get()); 402 dllSvc->DisableFull(); 403 } 404 # endif // defined(XP_WIN) 405 406 ipc::CrashReporterClient::DestroySingleton(); 407 XRE_ShutdownChildProcess(); 408 }); 409 #endif // NS_FREE_PERMANENT_DATA 410 } 411 412 } // namespace mozilla::ipc