Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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,
nsTArray<gfx::GfxVarUpdate>&& aUpdates) {
PROFILER_MARKER_UNTYPED(
"UtilityProcessChild::RecvStartUtilityAudioDecoderService", MEDIA,
MarkerOptions(MarkerTiming::IntervalUntilNowFrom(mChildStartTime)));
mUtilityAudioDecoderInstance =
new UtilityAudioDecoderParent(std::move(aUpdates));
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