/* -*- 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 https://mozilla.org/MPL/2.0/. */ #ifndef mozilla_UntrustedModulesProcessor_h #define mozilla_UntrustedModulesProcessor_h #include "mozilla/Atomics.h" #include "mozilla/DebugOnly.h" #include "mozilla/glue/WindowsDllServices.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" #include "mozilla/RefPtr.h" #include "mozilla/UntrustedModulesData.h" #include "mozilla/Vector.h" #include "mozilla/WinHeaderOnlyUtils.h" #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsIRunnable.h" #include "nsISupportsImpl.h" #include "nsString.h" namespace mozilla { class ModuleEvaluator; using UntrustedModulesPromise = MozPromise, nsresult, true>; using ModulesTrustPromise = MozPromise; using GetModulesTrustIpcPromise = MozPromise, ipc::ResponseRejectReason, true>; struct UnprocessedModuleLoadInfoContainer final : public LinkedListElement { glue::EnhancedModuleLoadInfo mInfo; template explicit UnprocessedModuleLoadInfoContainer(T&& aInfo) : mInfo(std::move(aInfo)) {} UnprocessedModuleLoadInfoContainer( const UnprocessedModuleLoadInfoContainer&) = delete; UnprocessedModuleLoadInfoContainer& operator=( const UnprocessedModuleLoadInfoContainer&) = delete; }; using UnprocessedModuleLoads = AutoCleanLinkedList; class UntrustedModulesProcessor final : public nsIObserver, public nsIThreadPoolListener { public: static RefPtr Create( bool aIsReadyForBackgroundProcessing); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIOBSERVER NS_DECL_NSITHREADPOOLLISTENER // Called to check if the parent process is ready when a child process // is spanwed bool IsReadyForBackgroundProcessing() const; // Called by DLL Services to explicitly begin shutting down void Disable(); // Called by DLL Services to submit module load data to the processor void Enqueue(glue::EnhancedModuleLoadInfo&& aModLoadInfo); void Enqueue(ModuleLoadInfoVec&& aEvents); // Called by telemetry to retrieve the processed data RefPtr GetProcessedData(); // Called by IPC actors in the parent process to evaluate module trust // on behalf of child processes RefPtr GetModulesTrust(ModulePaths&& aModPaths, bool aRunAtNormalPriority); UntrustedModulesProcessor(const UntrustedModulesProcessor&) = delete; UntrustedModulesProcessor(UntrustedModulesProcessor&&) = delete; UntrustedModulesProcessor& operator=(const UntrustedModulesProcessor&) = delete; UntrustedModulesProcessor& operator=(UntrustedModulesProcessor&&) = delete; private: ~UntrustedModulesProcessor() = default; explicit UntrustedModulesProcessor(bool aIsReadyForBackgroundProcessing); static bool IsSupportedProcessType(); void AddObservers(); void RemoveObservers(); void ScheduleNonEmptyQueueProcessing(const MutexAutoLock& aProofOfLock) MOZ_REQUIRES(mUnprocessedMutex); void CancelScheduledProcessing(const MutexAutoLock& aProofOfLock) MOZ_REQUIRES(mUnprocessedMutex); void DispatchBackgroundProcessing(); void BackgroundProcessModuleLoadQueue(); void ProcessModuleLoadQueue(); // Extract the loading events from mUnprocessedModuleLoads to process and // move to mProcessedModuleLoads. It's guaranteed that the total length of // mProcessedModuleLoads will not exceed |aMaxLength|. UnprocessedModuleLoads ExtractLoadingEventsToProcess(size_t aMaxLength); class ModulesMapResultWithLoads final { public: ModulesMapResultWithLoads(Maybe&& aModMapResult, UnprocessedModuleLoads&& aLoads) : mModMapResult(std::move(aModMapResult)), mLoads(std::move(aLoads)) {} Maybe mModMapResult; UnprocessedModuleLoads mLoads; }; using GetModulesTrustPromise = MozPromise, nsresult, true>; enum class Priority { Default, Background }; RefPtr ProcessModuleLoadQueueChildProcess( Priority aPriority); void BackgroundProcessModuleLoadQueueChildProcess(); void AssertRunningOnLazyIdleThread(); RefPtr GetProcessedDataInternal(); RefPtr GetProcessedDataInternalChildProcess(); RefPtr GetModulesTrustInternal( ModulePaths&& aModPaths, bool aRunAtNormalPriority); RefPtr GetModulesTrustInternal(ModulePaths&& aModPaths); // This function is only called by the parent process RefPtr GetOrAddModuleRecord(const ModuleEvaluator& aModEval, const nsAString& aResolvedNtPath); // Only called by child processes RefPtr GetModuleRecord( const ModulesMap& aModules, const glue::EnhancedModuleLoadInfo& aModuleLoadInfo); RefPtr SendGetModulesTrust(ModulePaths&& aModules, Priority aPriority); void CompleteProcessing(ModulesMapResultWithLoads&& aModulesAndLoads); RefPtr GetAllProcessedData(const char* aSource); private: RefPtr mThread; Mutex mThreadHandleMutex; Mutex mUnprocessedMutex; Mutex mModuleCacheMutex; // Windows HANDLE for the currently active mThread, if active. nsAutoHandle mThreadHandle MOZ_GUARDED_BY(mThreadHandleMutex); // The members in this group are protected by mUnprocessedMutex UnprocessedModuleLoads mUnprocessedModuleLoads MOZ_GUARDED_BY(mUnprocessedMutex); nsCOMPtr mIdleRunnable MOZ_GUARDED_BY(mUnprocessedMutex); // This member must only be touched on mThread UntrustedModulesData mProcessedModuleLoads; enum class Status { StartingUp, Allowed, ShuttingDown }; // This member may be touched by any thread Atomic mStatus; // Cache all module records, including ones trusted and ones loaded in // child processes, in the browser process to avoid evaluating the same // module multiple times ModulesMap mGlobalModuleCache MOZ_GUARDED_BY(mModuleCacheMutex); }; } // namespace mozilla #endif // mozilla_UntrustedModulesProcessor_h