no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / toolkit / xre / dllservices / UntrustedModulesProcessor.cpp
blob1d1183735541379e2b601726e1432f9c3c167220
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 https://mozilla.org/MPL/2.0/. */
7 #include "UntrustedModulesProcessor.h"
9 #include <windows.h>
11 #include "GMPPlatform.h"
12 #include "GMPServiceParent.h"
13 #include "mozilla/CmdLineAndEnvUtils.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/dom/ContentChild.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/Likely.h"
18 #include "mozilla/net/SocketProcessChild.h"
19 #include "mozilla/net/SocketProcessParent.h"
20 #include "mozilla/ipc/UtilityProcessParent.h"
21 #include "mozilla/ipc/UtilityProcessChild.h"
22 #include "mozilla/RDDChild.h"
23 #include "mozilla/RDDParent.h"
24 #include "mozilla/RDDProcessManager.h"
25 #include "mozilla/ScopeExit.h"
26 #include "mozilla/Services.h"
27 #include "mozilla/Telemetry.h"
28 #include "mozilla/Unused.h"
29 #include "ModuleEvaluator.h"
30 #include "nsCOMPtr.h"
31 #include "nsHashKeys.h"
32 #include "nsIObserverService.h"
33 #include "nsTHashtable.h"
34 #include "nsThreadUtils.h"
35 #include "nsXULAppAPI.h"
36 #include "private/prpriv.h" // For PR_GetThreadID
38 namespace mozilla {
40 class MOZ_RAII BackgroundPriorityRegion final {
41 public:
42 BackgroundPriorityRegion()
43 : mIsBackground(
44 ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_IDLE)) {}
46 ~BackgroundPriorityRegion() {
47 if (!mIsBackground) {
48 return;
51 Clear(::GetCurrentThread());
54 static void Clear(const nsAutoHandle& aThread) {
55 if (!aThread) {
56 return;
59 Clear(aThread.get());
62 BackgroundPriorityRegion(const BackgroundPriorityRegion&) = delete;
63 BackgroundPriorityRegion(BackgroundPriorityRegion&&) = delete;
64 BackgroundPriorityRegion& operator=(const BackgroundPriorityRegion&) = delete;
65 BackgroundPriorityRegion& operator=(BackgroundPriorityRegion&&) = delete;
67 private:
68 static void Clear(HANDLE aThread) {
69 DebugOnly<BOOL> ok = ::SetThreadPriority(aThread, THREAD_PRIORITY_NORMAL);
70 MOZ_ASSERT(ok);
73 private:
74 const BOOL mIsBackground;
77 /* static */
78 bool UntrustedModulesProcessor::IsSupportedProcessType() {
79 switch (XRE_GetProcessType()) {
80 case GeckoProcessType_Default:
81 case GeckoProcessType_Content:
82 case GeckoProcessType_Socket:
83 return Telemetry::CanRecordReleaseData();
84 case GeckoProcessType_RDD:
85 case GeckoProcessType_Utility:
86 case GeckoProcessType_GMPlugin:
87 // For GMPlugin, RDD and Utility process, we check the telemetry settings
88 // in RDDChild::Init() / UtilityProcessChild::Init() / GMPChild::Init()
89 // running in the browser process because CanRecordReleaseData() always
90 // returns false here.
91 return true;
92 default:
93 return false;
97 /* static */
98 RefPtr<UntrustedModulesProcessor> UntrustedModulesProcessor::Create(
99 bool aIsReadyForBackgroundProcessing) {
100 if (!IsSupportedProcessType()) {
101 return nullptr;
104 RefPtr<UntrustedModulesProcessor> result(
105 new UntrustedModulesProcessor(aIsReadyForBackgroundProcessing));
106 return result.forget();
109 NS_IMPL_ISUPPORTS(UntrustedModulesProcessor, nsIObserver, nsIThreadPoolListener)
111 static const uint32_t kThreadTimeoutMS = 120000; // 2 minutes
113 UntrustedModulesProcessor::UntrustedModulesProcessor(
114 bool aIsReadyForBackgroundProcessing)
115 : mThread(new LazyIdleThread(kThreadTimeoutMS, "Untrusted Modules",
116 LazyIdleThread::ManualShutdown)),
117 mThreadHandleMutex(
118 "mozilla::UntrustedModulesProcessor::mThreadHandleMutex"),
119 mUnprocessedMutex(
120 "mozilla::UntrustedModulesProcessor::mUnprocessedMutex"),
121 mModuleCacheMutex(
122 "mozilla::UntrustedModulesProcessor::mModuleCacheMutex"),
123 mStatus(aIsReadyForBackgroundProcessing ? Status::Allowed
124 : Status::StartingUp) {
125 AddObservers();
128 void UntrustedModulesProcessor::AddObservers() {
129 nsCOMPtr<nsIObserverService> obsServ(services::GetObserverService());
130 obsServ->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
131 obsServ->AddObserver(this, "xpcom-shutdown-threads", false);
132 obsServ->AddObserver(this, "unblock-untrusted-modules-thread", false);
133 if (XRE_IsContentProcess()) {
134 obsServ->AddObserver(this, "content-child-will-shutdown", false);
136 mThread->SetListener(this);
139 bool UntrustedModulesProcessor::IsReadyForBackgroundProcessing() const {
140 return mStatus == Status::Allowed;
143 void UntrustedModulesProcessor::Disable() {
144 // Ensure that mThread cannot run at low priority anymore
146 MutexAutoLock lock(mThreadHandleMutex);
147 BackgroundPriorityRegion::Clear(mThreadHandle);
150 // No more background processing allowed beyond this point
151 if (mStatus.exchange(Status::ShuttingDown) != Status::Allowed) {
152 return;
155 MutexAutoLock lock(mUnprocessedMutex);
156 CancelScheduledProcessing(lock);
159 NS_IMETHODIMP UntrustedModulesProcessor::Observe(nsISupports* aSubject,
160 const char* aTopic,
161 const char16_t* aData) {
162 if (!strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID) ||
163 !strcmp(aTopic, "content-child-will-shutdown")) {
164 Disable();
165 return NS_OK;
168 if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
169 Disable();
170 mThread->Shutdown();
172 RemoveObservers();
174 mThread = nullptr;
175 return NS_OK;
178 if (!strcmp(aTopic, "unblock-untrusted-modules-thread")) {
179 nsCOMPtr<nsIObserverService> obs(services::GetObserverService());
180 obs->RemoveObserver(this, "unblock-untrusted-modules-thread");
182 mStatus.compareExchange(Status::StartingUp, Status::Allowed);
184 if (!IsReadyForBackgroundProcessing()) {
185 // If we're shutting down, stop here.
186 return NS_OK;
189 if (XRE_IsParentProcess()) {
190 // Propagate notification to child processes
191 nsTArray<dom::ContentParent*> contentProcesses;
192 dom::ContentParent::GetAll(contentProcesses);
193 for (auto* proc : contentProcesses) {
194 Unused << proc->SendUnblockUntrustedModulesThread();
196 if (auto* proc = net::SocketProcessParent::GetSingleton()) {
197 Unused << proc->SendUnblockUntrustedModulesThread();
199 if (auto* rddMgr = RDDProcessManager::Get()) {
200 if (auto* proc = rddMgr->GetRDDChild()) {
201 Unused << proc->SendUnblockUntrustedModulesThread();
204 if (RefPtr<gmp::GeckoMediaPluginServiceParent> gmps =
205 gmp::GeckoMediaPluginServiceParent::GetSingleton()) {
206 gmps->SendUnblockUntrustedModulesThread();
210 return NS_OK;
213 MOZ_ASSERT_UNREACHABLE("Not reachable");
215 return NS_OK;
218 NS_IMETHODIMP UntrustedModulesProcessor::OnThreadCreated() {
219 // Whenever a backing lazy thread is created, record a thread handle to it.
220 HANDLE threadHandle;
221 if (!::DuplicateHandle(
222 ::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(),
223 &threadHandle,
224 THREAD_QUERY_LIMITED_INFORMATION | THREAD_SET_LIMITED_INFORMATION,
225 FALSE, 0)) {
226 MOZ_ASSERT_UNREACHABLE("DuplicateHandle failed on GetCurrentThread()?");
227 threadHandle = nullptr;
229 MutexAutoLock lock(mThreadHandleMutex);
230 mThreadHandle.own(threadHandle);
231 return NS_OK;
234 NS_IMETHODIMP UntrustedModulesProcessor::OnThreadShuttingDown() {
235 // When a lazy thread shuts down, clean up the thread handle reference unless
236 // it's already been replaced.
237 MutexAutoLock lock(mThreadHandleMutex);
238 if (mThreadHandle && ::GetCurrentThreadId() == ::GetThreadId(mThreadHandle)) {
239 mThreadHandle.reset();
241 return NS_OK;
244 void UntrustedModulesProcessor::RemoveObservers() {
245 nsCOMPtr<nsIObserverService> obsServ(services::GetObserverService());
246 obsServ->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
247 obsServ->RemoveObserver(this, "xpcom-shutdown-threads");
248 obsServ->RemoveObserver(this, "unblock-untrusted-modules-thread");
249 if (XRE_IsContentProcess()) {
250 obsServ->RemoveObserver(this, "content-child-will-shutdown");
252 mThread->SetListener(nullptr);
255 void UntrustedModulesProcessor::ScheduleNonEmptyQueueProcessing(
256 const MutexAutoLock& aProofOfLock) {
257 // In case something tried to load a DLL during shutdown
258 if (!mThread) {
259 return;
262 #if defined(ENABLE_TESTS)
263 // Don't bother scheduling background processing in short-lived xpcshell
264 // processes; it makes the test suites take too long.
265 if (MOZ_UNLIKELY(mozilla::EnvHasValue("XPCSHELL_TEST_PROFILE_DIR"))) {
266 return;
268 #endif // defined(ENABLE_TESTS)
270 if (mIdleRunnable) {
271 return;
274 if (!IsReadyForBackgroundProcessing()) {
275 return;
278 // Schedule a runnable to trigger background processing once the main thread
279 // has gone idle. We do it this way to ensure that we don't start doing a
280 // bunch of processing during periods of heavy main thread activity.
281 nsCOMPtr<nsIRunnable> idleRunnable(NewCancelableRunnableMethod(
282 "UntrustedModulesProcessor::DispatchBackgroundProcessing", this,
283 &UntrustedModulesProcessor::DispatchBackgroundProcessing));
285 if (NS_FAILED(NS_DispatchToMainThreadQueue(do_AddRef(idleRunnable),
286 EventQueuePriority::Idle))) {
287 return;
290 mIdleRunnable = std::move(idleRunnable);
293 void UntrustedModulesProcessor::CancelScheduledProcessing(
294 const MutexAutoLock& aProofOfLock) {
295 if (!mIdleRunnable) {
296 return;
299 nsCOMPtr<nsICancelableRunnable> cancelable(do_QueryInterface(mIdleRunnable));
300 if (cancelable) {
301 // Stop the pending idle runnable from doing anything
302 cancelable->Cancel();
305 mIdleRunnable = nullptr;
308 void UntrustedModulesProcessor::DispatchBackgroundProcessing() {
309 MOZ_ASSERT(NS_IsMainThread());
311 if (!IsReadyForBackgroundProcessing()) {
312 return;
315 nsCOMPtr<nsIRunnable> runnable(NewRunnableMethod(
316 "UntrustedModulesProcessor::BackgroundProcessModuleLoadQueue", this,
317 &UntrustedModulesProcessor::BackgroundProcessModuleLoadQueue));
319 mThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
322 void UntrustedModulesProcessor::Enqueue(
323 glue::EnhancedModuleLoadInfo&& aModLoadInfo) {
324 if (mStatus == Status::ShuttingDown) {
325 return;
329 MutexAutoLock lock(mThreadHandleMutex);
330 DWORD bgThreadId = ::GetThreadId(mThreadHandle);
331 if (aModLoadInfo.mNtLoadInfo.mThreadId == bgThreadId) {
332 // Exclude loads that were caused by our own background thread
333 return;
337 MutexAutoLock lock(mUnprocessedMutex);
339 mUnprocessedModuleLoads.insertBack(
340 new UnprocessedModuleLoadInfoContainer(std::move(aModLoadInfo)));
342 ScheduleNonEmptyQueueProcessing(lock);
345 void UntrustedModulesProcessor::Enqueue(ModuleLoadInfoVec&& aEvents) {
346 if (mStatus == Status::ShuttingDown) {
347 return;
350 // We do not need to attempt to exclude our background thread in this case
351 // because |aEvents| was accumulated prior to |mThread|'s existence.
353 MutexAutoLock lock(mUnprocessedMutex);
355 for (auto& event : aEvents) {
356 mUnprocessedModuleLoads.insertBack(
357 new UnprocessedModuleLoadInfoContainer(std::move(event)));
360 ScheduleNonEmptyQueueProcessing(lock);
363 void UntrustedModulesProcessor::AssertRunningOnLazyIdleThread() {
364 #if defined(DEBUG)
365 MOZ_ASSERT(mThread->IsOnCurrentThread());
366 #endif // defined(DEBUG)
369 RefPtr<UntrustedModulesPromise> UntrustedModulesProcessor::GetProcessedData() {
370 MOZ_ASSERT(NS_IsMainThread());
372 // Clear any background priority in case background processing is running.
374 MutexAutoLock lock(mThreadHandleMutex);
375 BackgroundPriorityRegion::Clear(mThreadHandle);
378 RefPtr<UntrustedModulesProcessor> self(this);
379 return InvokeAsync(mThread, __func__, [self = std::move(self)]() {
380 return self->GetProcessedDataInternal();
384 RefPtr<ModulesTrustPromise> UntrustedModulesProcessor::GetModulesTrust(
385 ModulePaths&& aModPaths, bool aRunAtNormalPriority) {
386 MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread());
388 if (!IsReadyForBackgroundProcessing()) {
389 return ModulesTrustPromise::CreateAndReject(
390 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
393 RefPtr<UntrustedModulesProcessor> self(this);
394 auto run = [self = std::move(self), modPaths = std::move(aModPaths),
395 runNormal = aRunAtNormalPriority]() mutable {
396 return self->GetModulesTrustInternal(std::move(modPaths), runNormal);
399 if (aRunAtNormalPriority) {
400 // Clear any background priority in case background processing is running.
402 MutexAutoLock lock(mThreadHandleMutex);
403 BackgroundPriorityRegion::Clear(mThreadHandle);
406 return InvokeAsync(mThread, __func__, std::move(run));
409 RefPtr<ModulesTrustPromise::Private> p(
410 new ModulesTrustPromise::Private(__func__));
411 nsCOMPtr<nsISerialEventTarget> evtTarget(mThread);
412 const char* source = __func__;
414 auto runWrap = [evtTarget = std::move(evtTarget), p, source,
415 run = std::move(run)]() mutable -> void {
416 InvokeAsync(evtTarget, source, std::move(run))->ChainTo(p.forget(), source);
419 nsCOMPtr<nsIRunnable> idleRunnable(
420 NS_NewRunnableFunction(source, std::move(runWrap)));
422 nsresult rv = NS_DispatchToMainThreadQueue(idleRunnable.forget(),
423 EventQueuePriority::Idle);
424 if (NS_FAILED(rv)) {
425 p->Reject(rv, source);
428 return p;
431 RefPtr<UntrustedModulesPromise>
432 UntrustedModulesProcessor::GetProcessedDataInternal() {
433 AssertRunningOnLazyIdleThread();
434 if (!XRE_IsParentProcess()) {
435 return GetProcessedDataInternalChildProcess();
438 ProcessModuleLoadQueue();
440 return GetAllProcessedData(__func__);
443 RefPtr<UntrustedModulesPromise> UntrustedModulesProcessor::GetAllProcessedData(
444 const char* aSource) {
445 AssertRunningOnLazyIdleThread();
447 UntrustedModulesData result;
449 if (!mProcessedModuleLoads) {
450 return UntrustedModulesPromise::CreateAndResolve(Nothing(), aSource);
453 result.Swap(mProcessedModuleLoads);
455 result.mElapsed = TimeStamp::Now() - TimeStamp::ProcessCreation();
457 return UntrustedModulesPromise::CreateAndResolve(
458 Some(UntrustedModulesData(std::move(result))), aSource);
461 RefPtr<UntrustedModulesPromise>
462 UntrustedModulesProcessor::GetProcessedDataInternalChildProcess() {
463 AssertRunningOnLazyIdleThread();
464 MOZ_ASSERT(!XRE_IsParentProcess());
466 RefPtr<GetModulesTrustPromise> whenProcessed(
467 ProcessModuleLoadQueueChildProcess(Priority::Default));
469 RefPtr<UntrustedModulesProcessor> self(this);
470 RefPtr<UntrustedModulesPromise::Private> p(
471 new UntrustedModulesPromise::Private(__func__));
472 nsCOMPtr<nsISerialEventTarget> evtTarget(mThread);
474 const char* source = __func__;
475 auto completionRoutine = [evtTarget = std::move(evtTarget), p,
476 self = std::move(self), source,
477 whenProcessed = std::move(whenProcessed)]() {
478 MOZ_ASSERT(NS_IsMainThread());
479 if (!self->IsReadyForBackgroundProcessing()) {
480 // We can't do any more work, just reject all the things
481 whenProcessed->Then(
482 GetMainThreadSerialEventTarget(), source,
483 [p, source](Maybe<ModulesMapResultWithLoads>&& aResult) {
484 p->Reject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN, source);
486 [p, source](nsresult aRv) { p->Reject(aRv, source); });
487 return;
490 whenProcessed->Then(
491 evtTarget, source,
492 [p, self = std::move(self),
493 source](Maybe<ModulesMapResultWithLoads>&& aResult) mutable {
494 if (aResult.isSome()) {
495 self->CompleteProcessing(std::move(aResult.ref()));
497 self->GetAllProcessedData(source)->ChainTo(p.forget(), source);
499 [p, source](nsresult aRv) { p->Reject(aRv, source); });
502 // We always send |completionRoutine| on a trip through the main thread
503 // due to some subtlety with |mThread| being a LazyIdleThread: we can only
504 // Dispatch or Then to |mThread| from its creating thread, which is the
505 // main thread. Hopefully we can get rid of this in the future and just
506 // invoke whenProcessed->Then() directly.
507 nsresult rv = NS_DispatchToMainThread(
508 NS_NewRunnableFunction(__func__, std::move(completionRoutine)));
509 MOZ_ASSERT(NS_SUCCEEDED(rv));
510 if (NS_FAILED(rv)) {
511 p->Reject(rv, __func__);
514 return p;
517 void UntrustedModulesProcessor::BackgroundProcessModuleLoadQueue() {
518 if (!IsReadyForBackgroundProcessing()) {
519 return;
522 BackgroundPriorityRegion bgRgn;
524 if (!IsReadyForBackgroundProcessing()) {
525 return;
528 ProcessModuleLoadQueue();
531 RefPtr<ModuleRecord> UntrustedModulesProcessor::GetOrAddModuleRecord(
532 const ModuleEvaluator& aModEval, const nsAString& aResolvedNtPath) {
533 MOZ_ASSERT(XRE_IsParentProcess());
535 MutexAutoLock lock(mModuleCacheMutex);
536 return mGlobalModuleCache.WithEntryHandle(
537 aResolvedNtPath, [&](auto&& addPtr) -> RefPtr<ModuleRecord> {
538 if (addPtr) {
539 return addPtr.Data();
542 RefPtr<ModuleRecord> newMod(new ModuleRecord(aResolvedNtPath));
543 if (!(*newMod)) {
544 return nullptr;
547 Maybe<ModuleTrustFlags> maybeTrust = aModEval.GetTrust(*newMod);
548 if (maybeTrust.isNothing()) {
549 return nullptr;
552 newMod->mTrustFlags = maybeTrust.value();
554 return addPtr.Insert(std::move(newMod));
558 RefPtr<ModuleRecord> UntrustedModulesProcessor::GetModuleRecord(
559 const ModulesMap& aModules,
560 const glue::EnhancedModuleLoadInfo& aModuleLoadInfo) {
561 MOZ_ASSERT(!XRE_IsParentProcess());
563 return aModules.Get(aModuleLoadInfo.mNtLoadInfo.mSectionName.AsString());
566 void UntrustedModulesProcessor::BackgroundProcessModuleLoadQueueChildProcess() {
567 RefPtr<GetModulesTrustPromise> whenProcessed(
568 ProcessModuleLoadQueueChildProcess(Priority::Background));
570 RefPtr<UntrustedModulesProcessor> self(this);
571 nsCOMPtr<nsISerialEventTarget> evtTarget(mThread);
573 const char* source = __func__;
574 auto completionRoutine = [evtTarget = std::move(evtTarget),
575 self = std::move(self), source,
576 whenProcessed = std::move(whenProcessed)]() {
577 MOZ_ASSERT(NS_IsMainThread());
578 if (!self->IsReadyForBackgroundProcessing()) {
579 // We can't do any more work, just no-op
580 whenProcessed->Then(
581 GetMainThreadSerialEventTarget(), source,
582 [](Maybe<ModulesMapResultWithLoads>&& aResult) {},
583 [](nsresult aRv) {});
584 return;
587 whenProcessed->Then(
588 evtTarget, source,
589 [self = std::move(self)](Maybe<ModulesMapResultWithLoads>&& aResult) {
590 if (aResult.isNothing() || !self->IsReadyForBackgroundProcessing()) {
591 // Nothing to do
592 return;
595 BackgroundPriorityRegion bgRgn;
596 self->CompleteProcessing(std::move(aResult.ref()));
598 [](nsresult aRv) {});
601 // We always send |completionRoutine| on a trip through the main thread
602 // due to some subtlety with |mThread| being a LazyIdleThread: we can only
603 // Dispatch or Then to |mThread| from its creating thread, which is the
604 // main thread. Hopefully we can get rid of this in the future and just
605 // invoke whenProcessed->Then() directly.
606 DebugOnly<nsresult> rv = NS_DispatchToMainThread(
607 NS_NewRunnableFunction(__func__, std::move(completionRoutine)));
608 MOZ_ASSERT(NS_SUCCEEDED(rv));
611 UnprocessedModuleLoads UntrustedModulesProcessor::ExtractLoadingEventsToProcess(
612 size_t aMaxLength) {
613 UnprocessedModuleLoads loadsToProcess;
615 MutexAutoLock lock(mUnprocessedMutex);
616 CancelScheduledProcessing(lock);
618 loadsToProcess.splice(0, mUnprocessedModuleLoads, 0, aMaxLength);
619 return loadsToProcess;
622 // This function contains multiple IsReadyForBackgroundProcessing() checks so
623 // that we can quickly bail out at the first sign of shutdown. This may be
624 // important when the current thread is running under background priority.
625 void UntrustedModulesProcessor::ProcessModuleLoadQueue() {
626 AssertRunningOnLazyIdleThread();
627 if (!XRE_IsParentProcess()) {
628 BackgroundProcessModuleLoadQueueChildProcess();
629 return;
632 UnprocessedModuleLoads loadsToProcess =
633 ExtractLoadingEventsToProcess(UntrustedModulesData::kMaxEvents);
634 if (!IsReadyForBackgroundProcessing() || loadsToProcess.isEmpty()) {
635 return;
638 ModuleEvaluator modEval;
639 MOZ_ASSERT(!!modEval);
640 if (!modEval) {
641 return;
644 Telemetry::BatchProcessedStackGenerator stackProcessor;
645 Maybe<double> maybeXulLoadDuration;
646 Vector<Telemetry::ProcessedStack> processedStacks;
647 UntrustedModuleLoadingEvents processedEvents;
648 uint32_t sanitizationFailures = 0;
649 uint32_t trustTestFailures = 0;
651 for (UnprocessedModuleLoadInfoContainer* container : loadsToProcess) {
652 glue::EnhancedModuleLoadInfo& entry = container->mInfo;
654 if (!IsReadyForBackgroundProcessing()) {
655 return;
658 RefPtr<ModuleRecord> module(GetOrAddModuleRecord(
659 modEval, entry.mNtLoadInfo.mSectionName.AsString()));
660 if (!module) {
661 // We failed to obtain trust information about the module.
662 // Don't include test failures in the ping to avoid flooding it.
663 ++trustTestFailures;
664 continue;
667 if (!IsReadyForBackgroundProcessing()) {
668 return;
671 glue::EnhancedModuleLoadInfo::BacktraceType backtrace =
672 std::move(entry.mNtLoadInfo.mBacktrace);
673 ProcessedModuleLoadEvent event(std::move(entry), std::move(module));
675 if (!event) {
676 // We don't have a sanitized DLL path, so we cannot include this event
677 // for privacy reasons.
678 ++sanitizationFailures;
679 continue;
682 if (!IsReadyForBackgroundProcessing()) {
683 return;
686 if (event.IsTrusted()) {
687 if (event.IsXULLoad()) {
688 maybeXulLoadDuration = event.mLoadDurationMS;
691 // Trusted modules are not included in the ping
692 continue;
695 mProcessedModuleLoads.mModules.LookupOrInsert(
696 event.mModule->mResolvedNtName, event.mModule);
698 if (!IsReadyForBackgroundProcessing()) {
699 return;
702 Telemetry::ProcessedStack processedStack =
703 stackProcessor.GetStackAndModules(backtrace);
705 if (!IsReadyForBackgroundProcessing()) {
706 return;
709 Unused << processedStacks.emplaceBack(std::move(processedStack));
710 processedEvents.insertBack(
711 new ProcessedModuleLoadEventContainer(std::move(event)));
714 if (processedStacks.empty() && processedEvents.isEmpty() &&
715 !sanitizationFailures && !trustTestFailures) {
716 // Nothing to save
717 return;
720 if (!IsReadyForBackgroundProcessing()) {
721 return;
724 // Modules have been added to mProcessedModuleLoads.mModules
725 // in the loop above. Passing an empty ModulesMap to AddNewLoads.
726 mProcessedModuleLoads.AddNewLoads(ModulesMap{}, std::move(processedEvents),
727 std::move(processedStacks));
728 if (maybeXulLoadDuration) {
729 MOZ_ASSERT(!mProcessedModuleLoads.mXULLoadDurationMS);
730 mProcessedModuleLoads.mXULLoadDurationMS = maybeXulLoadDuration;
733 mProcessedModuleLoads.mSanitizationFailures += sanitizationFailures;
734 mProcessedModuleLoads.mTrustTestFailures += trustTestFailures;
737 template <typename ActorT>
738 static RefPtr<GetModulesTrustIpcPromise> SendGetModulesTrust(
739 ActorT* aActor, ModulePaths&& aModPaths, bool aRunAtNormalPriority) {
740 MOZ_ASSERT(NS_IsMainThread());
741 return aActor->SendGetModulesTrust(std::move(aModPaths),
742 aRunAtNormalPriority);
745 RefPtr<GetModulesTrustIpcPromise>
746 UntrustedModulesProcessor::SendGetModulesTrust(ModulePaths&& aModules,
747 Priority aPriority) {
748 MOZ_ASSERT(NS_IsMainThread());
749 bool runNormal = aPriority == Priority::Default;
751 switch (XRE_GetProcessType()) {
752 case GeckoProcessType_Content: {
753 return ::mozilla::SendGetModulesTrust(dom::ContentChild::GetSingleton(),
754 std::move(aModules), runNormal);
756 case GeckoProcessType_RDD: {
757 return ::mozilla::SendGetModulesTrust(RDDParent::GetSingleton(),
758 std::move(aModules), runNormal);
760 case GeckoProcessType_Socket: {
761 return ::mozilla::SendGetModulesTrust(
762 net::SocketProcessChild::GetSingleton(), std::move(aModules),
763 runNormal);
765 case GeckoProcessType_Utility: {
766 return ::mozilla::SendGetModulesTrust(
767 ipc::UtilityProcessChild::GetSingleton().get(), std::move(aModules),
768 runNormal);
770 case GeckoProcessType_GMPlugin: {
771 return ::mozilla::gmp::SendGetModulesTrust(std::move(aModules),
772 runNormal);
774 default: {
775 MOZ_ASSERT_UNREACHABLE("Unsupported process type");
776 return GetModulesTrustIpcPromise::CreateAndReject(
777 ipc::ResponseRejectReason::SendError, __func__);
783 * This method works very similarly to ProcessModuleLoadQueue, with the
784 * exception that a sandboxed child process does not have sufficient rights to
785 * be able to evaluate a module's trustworthiness. Instead, we accumulate the
786 * resolved paths for all of the modules in this batch and send them to the
787 * parent to determine trustworthiness.
789 * The parent process returns a list of untrusted modules and invokes
790 * CompleteProcessing to handle the remainder of the process.
792 * By doing it this way, we minimize the amount of data that needs to be sent
793 * over IPC and avoid the need to process every load's metadata only
794 * to throw most of it away (since most modules will be trusted).
796 RefPtr<UntrustedModulesProcessor::GetModulesTrustPromise>
797 UntrustedModulesProcessor::ProcessModuleLoadQueueChildProcess(
798 UntrustedModulesProcessor::Priority aPriority) {
799 AssertRunningOnLazyIdleThread();
800 MOZ_ASSERT(!XRE_IsParentProcess());
802 UnprocessedModuleLoads loadsToProcess =
803 ExtractLoadingEventsToProcess(UntrustedModulesData::kMaxEvents);
804 if (loadsToProcess.isEmpty()) {
805 // Nothing to process
806 return GetModulesTrustPromise::CreateAndResolve(Nothing(), __func__);
809 if (!IsReadyForBackgroundProcessing()) {
810 return GetModulesTrustPromise::CreateAndReject(
811 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
814 nsTHashtable<nsStringCaseInsensitiveHashKey> moduleNtPathSet;
816 // Build a set of modules to be processed by the parent
817 for (UnprocessedModuleLoadInfoContainer* container : loadsToProcess) {
818 glue::EnhancedModuleLoadInfo& entry = container->mInfo;
820 if (!IsReadyForBackgroundProcessing()) {
821 return GetModulesTrustPromise::CreateAndReject(
822 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
825 moduleNtPathSet.PutEntry(entry.mNtLoadInfo.mSectionName.AsString());
828 if (!IsReadyForBackgroundProcessing()) {
829 return GetModulesTrustPromise::CreateAndReject(
830 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
833 MOZ_ASSERT(!moduleNtPathSet.IsEmpty());
834 if (moduleNtPathSet.IsEmpty()) {
835 // Nothing to process
836 return GetModulesTrustPromise::CreateAndResolve(Nothing(), __func__);
839 ModulePaths moduleNtPaths(std::move(moduleNtPathSet));
841 if (!IsReadyForBackgroundProcessing()) {
842 return GetModulesTrustPromise::CreateAndReject(
843 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
846 RefPtr<UntrustedModulesProcessor> self(this);
848 auto invoker = [self = std::move(self),
849 moduleNtPaths = std::move(moduleNtPaths),
850 priority = aPriority]() mutable {
851 return self->SendGetModulesTrust(std::move(moduleNtPaths), priority);
854 RefPtr<GetModulesTrustPromise::Private> p(
855 new GetModulesTrustPromise::Private(__func__));
857 if (!IsReadyForBackgroundProcessing()) {
858 p->Reject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
859 return p;
862 // Send the IPC request via the main thread
863 InvokeAsync(GetMainThreadSerialEventTarget(), __func__, std::move(invoker))
864 ->Then(
865 GetMainThreadSerialEventTarget(), __func__,
866 [p, loads = std::move(loadsToProcess)](
867 Maybe<ModulesMapResult>&& aResult) mutable {
868 ModulesMapResultWithLoads result(std::move(aResult),
869 std::move(loads));
870 p->Resolve(Some(ModulesMapResultWithLoads(std::move(result))),
871 __func__);
873 [p](ipc::ResponseRejectReason aReason) {
874 p->Reject(NS_ERROR_FAILURE, __func__);
877 return p;
880 void UntrustedModulesProcessor::CompleteProcessing(
881 UntrustedModulesProcessor::ModulesMapResultWithLoads&& aModulesAndLoads) {
882 MOZ_ASSERT(!XRE_IsParentProcess());
883 AssertRunningOnLazyIdleThread();
885 if (!IsReadyForBackgroundProcessing()) {
886 return;
889 if (aModulesAndLoads.mModMapResult.isNothing()) {
890 // No untrusted modules in this batch, nothing to save.
891 return;
894 // This map only contains information about modules deemed to be untrusted,
895 // plus xul.dll. Any module referenced by load requests that is *not* in the
896 // map is deemed to be trusted.
897 ModulesMap& modules = aModulesAndLoads.mModMapResult.ref().mModules;
898 const uint32_t& trustTestFailures =
899 aModulesAndLoads.mModMapResult.ref().mTrustTestFailures;
900 UnprocessedModuleLoads& loads = aModulesAndLoads.mLoads;
902 if (modules.IsEmpty() && !trustTestFailures) {
903 // No data, nothing to save.
904 return;
907 if (!IsReadyForBackgroundProcessing()) {
908 return;
911 Telemetry::BatchProcessedStackGenerator stackProcessor;
913 Maybe<double> maybeXulLoadDuration;
914 Vector<Telemetry::ProcessedStack> processedStacks;
915 UntrustedModuleLoadingEvents processedEvents;
916 uint32_t sanitizationFailures = 0;
918 if (!modules.IsEmpty()) {
919 for (UnprocessedModuleLoadInfoContainer* container : loads) {
920 glue::EnhancedModuleLoadInfo& item = container->mInfo;
921 if (!IsReadyForBackgroundProcessing()) {
922 return;
925 RefPtr<ModuleRecord> module(GetModuleRecord(modules, item));
926 if (!module) {
927 // If module is null then |item| is trusted
928 continue;
931 if (!IsReadyForBackgroundProcessing()) {
932 return;
935 glue::EnhancedModuleLoadInfo::BacktraceType backtrace =
936 std::move(item.mNtLoadInfo.mBacktrace);
937 ProcessedModuleLoadEvent event(std::move(item), std::move(module));
939 if (!IsReadyForBackgroundProcessing()) {
940 return;
943 if (!event) {
944 // We don't have a sanitized DLL path, so we cannot include this event
945 // for privacy reasons.
946 ++sanitizationFailures;
947 continue;
950 if (!IsReadyForBackgroundProcessing()) {
951 return;
954 if (event.IsXULLoad()) {
955 maybeXulLoadDuration = event.mLoadDurationMS;
956 // We saved the XUL load duration, but it is still trusted, so we
957 // continue.
958 continue;
961 if (!IsReadyForBackgroundProcessing()) {
962 return;
965 Telemetry::ProcessedStack processedStack =
966 stackProcessor.GetStackAndModules(backtrace);
968 Unused << processedStacks.emplaceBack(std::move(processedStack));
969 processedEvents.insertBack(
970 new ProcessedModuleLoadEventContainer(std::move(event)));
974 if (processedStacks.empty() && processedEvents.isEmpty() &&
975 !sanitizationFailures && !trustTestFailures) {
976 // Nothing to save
977 return;
980 if (!IsReadyForBackgroundProcessing()) {
981 return;
984 mProcessedModuleLoads.AddNewLoads(modules, std::move(processedEvents),
985 std::move(processedStacks));
986 if (maybeXulLoadDuration) {
987 MOZ_ASSERT(!mProcessedModuleLoads.mXULLoadDurationMS);
988 mProcessedModuleLoads.mXULLoadDurationMS = maybeXulLoadDuration;
991 mProcessedModuleLoads.mSanitizationFailures += sanitizationFailures;
992 mProcessedModuleLoads.mTrustTestFailures += trustTestFailures;
995 // The thread priority of this job should match the priority that the child
996 // process is running with, as specified by |aRunAtNormalPriority|.
997 RefPtr<ModulesTrustPromise> UntrustedModulesProcessor::GetModulesTrustInternal(
998 ModulePaths&& aModPaths, bool aRunAtNormalPriority) {
999 MOZ_ASSERT(XRE_IsParentProcess());
1000 AssertRunningOnLazyIdleThread();
1002 if (!IsReadyForBackgroundProcessing()) {
1003 return ModulesTrustPromise::CreateAndReject(
1004 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
1007 if (aRunAtNormalPriority) {
1008 return GetModulesTrustInternal(std::move(aModPaths));
1011 BackgroundPriorityRegion bgRgn;
1012 return GetModulesTrustInternal(std::move(aModPaths));
1015 // For each module in |aModPaths|, evaluate its trustworthiness and only send
1016 // ModuleRecords for untrusted modules back to the child process. We also save
1017 // XUL's ModuleRecord so that the child process may report XUL's load time.
1018 RefPtr<ModulesTrustPromise> UntrustedModulesProcessor::GetModulesTrustInternal(
1019 ModulePaths&& aModPaths) {
1020 MOZ_ASSERT(XRE_IsParentProcess());
1021 AssertRunningOnLazyIdleThread();
1023 ModulesMapResult result;
1025 ModulesMap& modMap = result.mModules;
1026 uint32_t& trustTestFailures = result.mTrustTestFailures;
1028 ModuleEvaluator modEval;
1029 MOZ_ASSERT(!!modEval);
1030 if (!modEval) {
1031 return ModulesTrustPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
1034 for (auto& resolvedNtPath :
1035 aModPaths.mModuleNtPaths.as<ModulePaths::VecType>()) {
1036 if (!IsReadyForBackgroundProcessing()) {
1037 return ModulesTrustPromise::CreateAndReject(
1038 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
1041 MOZ_ASSERT(!resolvedNtPath.IsEmpty());
1042 if (resolvedNtPath.IsEmpty()) {
1043 continue;
1046 RefPtr<ModuleRecord> module(GetOrAddModuleRecord(modEval, resolvedNtPath));
1047 if (!module) {
1048 // We failed to obtain trust information.
1049 ++trustTestFailures;
1050 continue;
1053 if (!IsReadyForBackgroundProcessing()) {
1054 return ModulesTrustPromise::CreateAndReject(
1055 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
1058 if (module->IsTrusted() && !module->IsXUL()) {
1059 // If the module is trusted we exclude it from results, unless it's XUL.
1060 // (We save XUL so that the child process may report XUL's load time)
1061 continue;
1064 if (!IsReadyForBackgroundProcessing()) {
1065 return ModulesTrustPromise::CreateAndReject(
1066 NS_ERROR_ILLEGAL_DURING_SHUTDOWN, __func__);
1069 modMap.InsertOrUpdate(resolvedNtPath, std::move(module));
1072 return ModulesTrustPromise::CreateAndResolve(std::move(result), __func__);
1075 } // namespace mozilla