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"
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"
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
40 class MOZ_RAII BackgroundPriorityRegion final
{
42 BackgroundPriorityRegion()
44 ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_IDLE
)) {}
46 ~BackgroundPriorityRegion() {
51 Clear(::GetCurrentThread());
54 static void Clear(const nsAutoHandle
& aThread
) {
62 BackgroundPriorityRegion(const BackgroundPriorityRegion
&) = delete;
63 BackgroundPriorityRegion(BackgroundPriorityRegion
&&) = delete;
64 BackgroundPriorityRegion
& operator=(const BackgroundPriorityRegion
&) = delete;
65 BackgroundPriorityRegion
& operator=(BackgroundPriorityRegion
&&) = delete;
68 static void Clear(HANDLE aThread
) {
69 DebugOnly
<BOOL
> ok
= ::SetThreadPriority(aThread
, THREAD_PRIORITY_NORMAL
);
74 const BOOL mIsBackground
;
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.
98 RefPtr
<UntrustedModulesProcessor
> UntrustedModulesProcessor::Create(
99 bool aIsReadyForBackgroundProcessing
) {
100 if (!IsSupportedProcessType()) {
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
)),
118 "mozilla::UntrustedModulesProcessor::mThreadHandleMutex"),
120 "mozilla::UntrustedModulesProcessor::mUnprocessedMutex"),
122 "mozilla::UntrustedModulesProcessor::mModuleCacheMutex"),
123 mStatus(aIsReadyForBackgroundProcessing
? Status::Allowed
124 : Status::StartingUp
) {
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
) {
155 MutexAutoLock
lock(mUnprocessedMutex
);
156 CancelScheduledProcessing(lock
);
159 NS_IMETHODIMP
UntrustedModulesProcessor::Observe(nsISupports
* aSubject
,
161 const char16_t
* aData
) {
162 if (!strcmp(aTopic
, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID
) ||
163 !strcmp(aTopic
, "content-child-will-shutdown")) {
168 if (!strcmp(aTopic
, "xpcom-shutdown-threads")) {
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.
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();
213 MOZ_ASSERT_UNREACHABLE("Not reachable");
218 NS_IMETHODIMP
UntrustedModulesProcessor::OnThreadCreated() {
219 // Whenever a backing lazy thread is created, record a thread handle to it.
221 if (!::DuplicateHandle(
222 ::GetCurrentProcess(), ::GetCurrentThread(), ::GetCurrentProcess(),
224 THREAD_QUERY_LIMITED_INFORMATION
| THREAD_SET_LIMITED_INFORMATION
,
226 MOZ_ASSERT_UNREACHABLE("DuplicateHandle failed on GetCurrentThread()?");
227 threadHandle
= nullptr;
229 MutexAutoLock
lock(mThreadHandleMutex
);
230 mThreadHandle
.own(threadHandle
);
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();
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
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"))) {
268 #endif // defined(ENABLE_TESTS)
274 if (!IsReadyForBackgroundProcessing()) {
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
))) {
290 mIdleRunnable
= std::move(idleRunnable
);
293 void UntrustedModulesProcessor::CancelScheduledProcessing(
294 const MutexAutoLock
& aProofOfLock
) {
295 if (!mIdleRunnable
) {
299 nsCOMPtr
<nsICancelableRunnable
> cancelable(do_QueryInterface(mIdleRunnable
));
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()) {
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
) {
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
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
) {
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() {
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
);
425 p
->Reject(rv
, source
);
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
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
); });
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
));
511 p
->Reject(rv
, __func__
);
517 void UntrustedModulesProcessor::BackgroundProcessModuleLoadQueue() {
518 if (!IsReadyForBackgroundProcessing()) {
522 BackgroundPriorityRegion bgRgn
;
524 if (!IsReadyForBackgroundProcessing()) {
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
> {
539 return addPtr
.Data();
542 RefPtr
<ModuleRecord
> newMod(new ModuleRecord(aResolvedNtPath
));
547 Maybe
<ModuleTrustFlags
> maybeTrust
= aModEval
.GetTrust(*newMod
);
548 if (maybeTrust
.isNothing()) {
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
581 GetMainThreadSerialEventTarget(), source
,
582 [](Maybe
<ModulesMapResultWithLoads
>&& aResult
) {},
583 [](nsresult aRv
) {});
589 [self
= std::move(self
)](Maybe
<ModulesMapResultWithLoads
>&& aResult
) {
590 if (aResult
.isNothing() || !self
->IsReadyForBackgroundProcessing()) {
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(
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();
632 UnprocessedModuleLoads loadsToProcess
=
633 ExtractLoadingEventsToProcess(UntrustedModulesData::kMaxEvents
);
634 if (!IsReadyForBackgroundProcessing() || loadsToProcess
.isEmpty()) {
638 ModuleEvaluator modEval
;
639 MOZ_ASSERT(!!modEval
);
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()) {
658 RefPtr
<ModuleRecord
> module(GetOrAddModuleRecord(
659 modEval
, entry
.mNtLoadInfo
.mSectionName
.AsString()));
661 // We failed to obtain trust information about the module.
662 // Don't include test failures in the ping to avoid flooding it.
667 if (!IsReadyForBackgroundProcessing()) {
671 glue::EnhancedModuleLoadInfo::BacktraceType backtrace
=
672 std::move(entry
.mNtLoadInfo
.mBacktrace
);
673 ProcessedModuleLoadEvent
event(std::move(entry
), std::move(module
));
676 // We don't have a sanitized DLL path, so we cannot include this event
677 // for privacy reasons.
678 ++sanitizationFailures
;
682 if (!IsReadyForBackgroundProcessing()) {
686 if (event
.IsTrusted()) {
687 if (event
.IsXULLoad()) {
688 maybeXulLoadDuration
= event
.mLoadDurationMS
;
691 // Trusted modules are not included in the ping
695 mProcessedModuleLoads
.mModules
.LookupOrInsert(
696 event
.mModule
->mResolvedNtName
, event
.mModule
);
698 if (!IsReadyForBackgroundProcessing()) {
702 Telemetry::ProcessedStack processedStack
=
703 stackProcessor
.GetStackAndModules(backtrace
);
705 if (!IsReadyForBackgroundProcessing()) {
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
) {
720 if (!IsReadyForBackgroundProcessing()) {
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
),
765 case GeckoProcessType_Utility
: {
766 return ::mozilla::SendGetModulesTrust(
767 ipc::UtilityProcessChild::GetSingleton().get(), std::move(aModules
),
770 case GeckoProcessType_GMPlugin
: {
771 return ::mozilla::gmp::SendGetModulesTrust(std::move(aModules
),
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__
);
862 // Send the IPC request via the main thread
863 InvokeAsync(GetMainThreadSerialEventTarget(), __func__
, std::move(invoker
))
865 GetMainThreadSerialEventTarget(), __func__
,
866 [p
, loads
= std::move(loadsToProcess
)](
867 Maybe
<ModulesMapResult
>&& aResult
) mutable {
868 ModulesMapResultWithLoads
result(std::move(aResult
),
870 p
->Resolve(Some(ModulesMapResultWithLoads(std::move(result
))),
873 [p
](ipc::ResponseRejectReason aReason
) {
874 p
->Reject(NS_ERROR_FAILURE
, __func__
);
880 void UntrustedModulesProcessor::CompleteProcessing(
881 UntrustedModulesProcessor::ModulesMapResultWithLoads
&& aModulesAndLoads
) {
882 MOZ_ASSERT(!XRE_IsParentProcess());
883 AssertRunningOnLazyIdleThread();
885 if (!IsReadyForBackgroundProcessing()) {
889 if (aModulesAndLoads
.mModMapResult
.isNothing()) {
890 // No untrusted modules in this batch, nothing to save.
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.
907 if (!IsReadyForBackgroundProcessing()) {
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()) {
925 RefPtr
<ModuleRecord
> module(GetModuleRecord(modules
, item
));
927 // If module is null then |item| is trusted
931 if (!IsReadyForBackgroundProcessing()) {
935 glue::EnhancedModuleLoadInfo::BacktraceType backtrace
=
936 std::move(item
.mNtLoadInfo
.mBacktrace
);
937 ProcessedModuleLoadEvent
event(std::move(item
), std::move(module
));
939 if (!IsReadyForBackgroundProcessing()) {
944 // We don't have a sanitized DLL path, so we cannot include this event
945 // for privacy reasons.
946 ++sanitizationFailures
;
950 if (!IsReadyForBackgroundProcessing()) {
954 if (event
.IsXULLoad()) {
955 maybeXulLoadDuration
= event
.mLoadDurationMS
;
956 // We saved the XUL load duration, but it is still trusted, so we
961 if (!IsReadyForBackgroundProcessing()) {
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
) {
980 if (!IsReadyForBackgroundProcessing()) {
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
);
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()) {
1046 RefPtr
<ModuleRecord
> module(GetOrAddModuleRecord(modEval
, resolvedNtPath
));
1048 // We failed to obtain trust information.
1049 ++trustTestFailures
;
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)
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