diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /ipc/glue/UtilityProcessChild.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/glue/UtilityProcessChild.cpp')
-rw-r--r-- | ipc/glue/UtilityProcessChild.cpp | 401 |
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 |