summaryrefslogtreecommitdiffstats
path: root/ipc/glue/UtilityProcessChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/UtilityProcessChild.cpp')
-rw-r--r--ipc/glue/UtilityProcessChild.cpp401
1 files changed, 401 insertions, 0 deletions
diff --git a/ipc/glue/UtilityProcessChild.cpp b/ipc/glue/UtilityProcessChild.cpp
new file mode 100644
index 0000000000..cda3dbc817
--- /dev/null
+++ b/ipc/glue/UtilityProcessChild.cpp
@@ -0,0 +1,401 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "UtilityProcessChild.h"
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/Logging.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/JSOracleChild.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/UtilityProcessManager.h"
+#include "mozilla/ipc/UtilityProcessSandboxing.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RemoteDecoderManagerParent.h"
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+# include "mozilla/Sandbox.h"
+#endif
+
+#if defined(XP_OPENBSD) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+#endif
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+# include "mozilla/SandboxTestingChild.h"
+#endif
+
+#include "mozilla/Telemetry.h"
+
+#if defined(XP_WIN)
+# include "mozilla/WinDllServices.h"
+# include "mozilla/dom/WindowsUtilsChild.h"
+# include "mozilla/widget/filedialog/WinFileDialogChild.h"
+#endif
+
+#include "nsDebugImpl.h"
+#include "nsIXULRuntime.h"
+#include "nsThreadManager.h"
+#include "GeckoProfiler.h"
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/FOGIPC.h"
+#include "mozilla/glean/GleanMetrics.h"
+
+#include "mozilla/Services.h"
+
+namespace mozilla::ipc {
+
+using namespace layers;
+
+static StaticMutex sUtilityProcessChildMutex;
+static StaticRefPtr<UtilityProcessChild> sUtilityProcessChild
+ MOZ_GUARDED_BY(sUtilityProcessChildMutex);
+
+UtilityProcessChild::UtilityProcessChild() : mChildStartTime(TimeStamp::Now()) {
+ nsDebugImpl::SetMultiprocessMode("Utility");
+}
+
+UtilityProcessChild::~UtilityProcessChild() = default;
+
+/* static */
+RefPtr<UtilityProcessChild> UtilityProcessChild::GetSingleton() {
+ MOZ_ASSERT(XRE_IsUtilityProcess());
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
+ return nullptr;
+ }
+ StaticMutexAutoLock lock(sUtilityProcessChildMutex);
+ if (!sUtilityProcessChild) {
+ sUtilityProcessChild = new UtilityProcessChild();
+ }
+ return sUtilityProcessChild;
+}
+
+/* static */
+RefPtr<UtilityProcessChild> UtilityProcessChild::Get() {
+ StaticMutexAutoLock lock(sUtilityProcessChildMutex);
+ return sUtilityProcessChild;
+}
+
+bool UtilityProcessChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const nsCString& aParentBuildID,
+ uint64_t aSandboxingKind) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Initialize the thread manager before starting IPC. Otherwise, messages
+ // may be posted to the main thread and we won't be able to process them.
+ if (NS_WARN_IF(NS_FAILED(nsThreadManager::get().Init()))) {
+ return false;
+ }
+
+ // Now it's safe to start IPC.
+ if (NS_WARN_IF(!aEndpoint.Bind(this))) {
+ return false;
+ }
+
+ // This must be checked before any IPDL message, which may hit sentinel
+ // errors due to parent and content processes having different
+ // versions.
+ MessageChannel* channel = GetIPCChannel();
+ if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID.get())) {
+ // We need to quit this process if the buildID doesn't match the parent's.
+ // This can occur when an update occurred in the background.
+ ipc::ProcessChild::QuickExit();
+ }
+
+ // Init crash reporter support.
+ ipc::CrashReporterClient::InitSingleton(this);
+
+ if (NS_FAILED(NS_InitMinimalXPCOM())) {
+ return false;
+ }
+
+ mSandbox = (SandboxingKind)aSandboxingKind;
+
+ // At the moment, only ORB uses JSContext in the
+ // Utility Process and ORB uses GENERIC_UTILITY
+ if (mSandbox == SandboxingKind::GENERIC_UTILITY) {
+ if (!JS_FrontendOnlyInit()) {
+ return false;
+ }
+#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
+ // Bug 1823458: delay pledge initialization, otherwise
+ // JS_FrontendOnlyInit triggers sysctl(KERN_PROC_ID) which isnt
+ // permitted with the current pledge.utility config
+ StartOpenBSDSandbox(GeckoProcessType_Utility, mSandbox);
+#endif
+ }
+
+ profiler_set_process_name(nsCString("Utility Process"));
+
+ // Notify the parent process that we have finished our init and that it can
+ // now resolve the pending promise of process startup
+ SendInitCompleted();
+
+ PROFILER_MARKER_UNTYPED(
+ "UtilityProcessChild::SendInitCompleted", IPC,
+ MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
+
+ RunOnShutdown(
+ [sandboxKind = mSandbox] {
+ StaticMutexAutoLock lock(sUtilityProcessChildMutex);
+ sUtilityProcessChild = nullptr;
+ if (sandboxKind == SandboxingKind::GENERIC_UTILITY) {
+ JS_FrontendOnlyShutDown();
+ }
+ },
+ ShutdownPhase::XPCOMShutdownFinal);
+
+ return true;
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+extern "C" {
+void CGSShutdownServerConnections();
+};
+#endif
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvInit(
+ const Maybe<FileDescriptor>& aBrokerFd,
+ const bool& aCanRecordReleaseTelemetry,
+ const bool& aIsReadyForBackgroundProcessing) {
+ // Do this now (before closing WindowServer on macOS) to avoid risking
+ // blocking in GetCurrentProcess() called on that platform
+ mozilla::ipc::SetThisProcessName("Utility Process");
+
+#if defined(MOZ_SANDBOX)
+# if defined(XP_MACOSX)
+ // Close all current connections to the WindowServer. This ensures that the
+ // Activity Monitor will not label the content process as "Not responding"
+ // because it's not running a native event loop. See bug 1384336.
+ CGSShutdownServerConnections();
+
+# elif defined(XP_LINUX)
+ int fd = -1;
+ if (aBrokerFd.isSome()) {
+ fd = aBrokerFd.value().ClonePlatformHandle().release();
+ }
+
+ SetUtilitySandbox(fd, mSandbox);
+
+# endif // XP_MACOSX/XP_LINUX
+#endif // MOZ_SANDBOX
+
+#if defined(XP_WIN)
+ if (aCanRecordReleaseTelemetry) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing);
+ }
+#endif // defined(XP_WIN)
+
+ PROFILER_MARKER_UNTYPED(
+ "UtilityProcessChild::RecvInit", IPC,
+ MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvPreferenceUpdate(
+ const Pref& aPref) {
+ Preferences::SetPreference(aPref);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint) {
+ mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile,
+ const RequestMemoryReportResolver& aResolver) {
+ nsPrintfCString processName("Utility (pid %" PRIPID
+ ", sandboxingKind %" PRIu64 ")",
+ base::GetCurrentProcId(), mSandbox);
+
+ mozilla::dom::MemoryReportRequestClient::Start(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, processName,
+ [&](const MemoryReport& aReport) {
+ Unused << GetSingleton()->SendAddMemoryReport(aReport);
+ },
+ aResolver);
+ return IPC_OK();
+}
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+mozilla::ipc::IPCResult UtilityProcessChild::RecvInitSandboxTesting(
+ Endpoint<PSandboxTestingChild>&& aEndpoint) {
+ if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
+ return IPC_FAIL(
+ this, "InitSandboxTesting failed to initialise the child process.");
+ }
+ return IPC_OK();
+}
+#endif
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvFlushFOGData(
+ FlushFOGDataResolver&& aResolver) {
+ glean::FlushFOGData(std::move(aResolver));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTriggerMetrics(
+ TestTriggerMetricsResolver&& aResolve) {
+ mozilla::glean::test_only_ipc::a_counter.Add(
+ nsIXULRuntime::PROCESS_TYPE_UTILITY);
+ aResolve(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvTestTelemetryProbes() {
+ const uint32_t kExpectedUintValue = 42;
+ Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UTILITY_ONLY_UINT,
+ kExpectedUintValue);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UtilityProcessChild::RecvStartUtilityAudioDecoderService(
+ Endpoint<PUtilityAudioDecoderParent>&& aEndpoint) {
+ PROFILER_MARKER_UNTYPED(
+ "UtilityProcessChild::RecvStartUtilityAudioDecoderService", MEDIA,
+ MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
+ mUtilityAudioDecoderInstance = new UtilityAudioDecoderParent();
+ if (!mUtilityAudioDecoderInstance) {
+ return IPC_FAIL(this, "Failed to create UtilityAudioDecoderParent");
+ }
+
+ mUtilityAudioDecoderInstance->Start(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvStartJSOracleService(
+ Endpoint<PJSOracleChild>&& aEndpoint) {
+ PROFILER_MARKER_UNTYPED(
+ "UtilityProcessChild::RecvStartJSOracleService", JS,
+ MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
+ mJSOracleInstance = new mozilla::dom::JSOracleChild();
+ if (!mJSOracleInstance) {
+ return IPC_FAIL(this, "Failed to create JSOracleParent");
+ }
+
+ mJSOracleInstance->Start(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWindowsUtilsService(
+ Endpoint<dom::PWindowsUtilsChild>&& aEndpoint) {
+ PROFILER_MARKER_UNTYPED(
+ "UtilityProcessChild::RecvStartWindowsUtilsService", OTHER,
+ MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
+ mWindowsUtilsInstance = new dom::WindowsUtilsChild();
+ if (!mWindowsUtilsInstance) {
+ return IPC_FAIL(this, "Failed to create WindowsUtilsChild");
+ }
+
+ [[maybe_unused]] bool ok = std::move(aEndpoint).Bind(mWindowsUtilsInstance);
+ MOZ_ASSERT(ok);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvStartWinFileDialogService(
+ Endpoint<widget::filedialog::PWinFileDialogChild>&& aEndpoint) {
+ PROFILER_MARKER_UNTYPED(
+ "UtilityProcessChild::RecvStartWinFileDialogService", OTHER,
+ MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
+
+ auto instance = MakeRefPtr<widget::filedialog::WinFileDialogChild>();
+ if (!instance) {
+ return IPC_FAIL(this, "Failed to create WinFileDialogChild");
+ }
+
+ bool const ok = std::move(aEndpoint).Bind(instance.get());
+ if (!ok) {
+ return IPC_FAIL(this, "Failed to bind created WinFileDialogChild");
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult UtilityProcessChild::RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetUntrustedModulesData()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](Maybe<UntrustedModulesData>&& aData) {
+ aResolver(std::move(aData));
+ },
+ [aResolver](nsresult aReason) { aResolver(Nothing()); });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+UtilityProcessChild::RecvUnblockUntrustedModulesThread() {
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr);
+ }
+ return IPC_OK();
+}
+#endif // defined(XP_WIN)
+
+void UtilityProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (AbnormalShutdown == aWhy) {
+ NS_WARNING("Shutting down Utility process early due to a crash!");
+ ipc::ProcessChild::QuickExit();
+ }
+
+ // Send the last bits of Glean data over to the main process.
+ glean::FlushFOGData(
+ [](ByteBuf&& aBuf) { glean::SendFOGData(std::move(aBuf)); });
+
+#ifndef NS_FREE_PERMANENT_DATA
+ ProcessChild::QuickExit();
+#else
+
+ if (mProfilerController) {
+ mProfilerController->Shutdown();
+ mProfilerController = nullptr;
+ }
+
+ uint32_t timeout = 0;
+ if (mUtilityAudioDecoderInstance) {
+ mUtilityAudioDecoderInstance = nullptr;
+ timeout = 10 * 1000;
+ }
+
+ mJSOracleInstance = nullptr;
+
+# ifdef XP_WIN
+ mWindowsUtilsInstance = nullptr;
+# endif
+
+ // Wait until all RemoteDecoderManagerParent have closed.
+ // It is still possible some may not have clean up yet, and we might hit
+ // timeout. Our xpcom-shutdown listener should take care of cleaning the
+ // reference of our singleton.
+ //
+ // FIXME: Should move from using AsyncBlockers to proper
+ // nsIAsyncShutdownService once it is not JS, see bug 1760855
+ mShutdownBlockers.WaitUntilClear(timeout)->Then(
+ GetCurrentSerialEventTarget(), __func__, [&]() {
+# ifdef XP_WIN
+ {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->DisableFull();
+ }
+# endif // defined(XP_WIN)
+
+ ipc::CrashReporterClient::DestroySingleton();
+ XRE_ShutdownChildProcess();
+ });
+#endif // NS_FREE_PERMANENT_DATA
+}
+
+} // namespace mozilla::ipc