summaryrefslogtreecommitdiffstats
path: root/toolkit/xre/dllservices/UntrustedModulesData.h
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/xre/dllservices/UntrustedModulesData.h')
-rw-r--r--toolkit/xre/dllservices/UntrustedModulesData.h662
1 files changed, 662 insertions, 0 deletions
diff --git a/toolkit/xre/dllservices/UntrustedModulesData.h b/toolkit/xre/dllservices/UntrustedModulesData.h
new file mode 100644
index 0000000000..9268728aa6
--- /dev/null
+++ b/toolkit/xre/dllservices/UntrustedModulesData.h
@@ -0,0 +1,662 @@
+/* -*- 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_UntrustedModulesData_h
+#define mozilla_UntrustedModulesData_h
+
+#if defined(XP_WIN)
+
+# include "ipc/IPCMessageUtils.h"
+# include "mozilla/CombinedStacks.h"
+# include "mozilla/DebugOnly.h"
+# include "mozilla/LinkedList.h"
+# include "mozilla/Maybe.h"
+# include "mozilla/RefPtr.h"
+# include "mozilla/TypedEnumBits.h"
+# include "mozilla/Unused.h"
+# include "mozilla/Variant.h"
+# include "mozilla/Vector.h"
+# include "mozilla/WinHeaderOnlyUtils.h"
+# include "nsCOMPtr.h"
+# include "nsHashKeys.h"
+# include "nsIFile.h"
+# include "nsISupportsImpl.h"
+# include "nsRefPtrHashtable.h"
+# include "nsString.h"
+# include "nsXULAppAPI.h"
+
+namespace mozilla {
+namespace glue {
+struct EnhancedModuleLoadInfo;
+} // namespace glue
+
+enum class ModuleTrustFlags : uint32_t {
+ None = 0,
+ MozillaSignature = 1,
+ MicrosoftWindowsSignature = 2,
+ MicrosoftVersion = 4,
+ FirefoxDirectory = 8,
+ FirefoxDirectoryAndVersion = 0x10,
+ SystemDirectory = 0x20,
+ KeyboardLayout = 0x40,
+ JitPI = 0x80,
+ WinSxSDirectory = 0x100,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ModuleTrustFlags);
+
+class VendorInfo final {
+ public:
+ enum class Source : uint32_t {
+ None,
+ Signature,
+ VersionInfo,
+ };
+
+ VendorInfo() : mSource(Source::None) {}
+ VendorInfo(const Source aSource, const nsAString& aVendor,
+ bool aHasNestedMicrosoftSignature)
+ : mSource(aSource),
+ mVendor(aVendor),
+ mHasNestedMicrosoftSignature(aHasNestedMicrosoftSignature) {
+ MOZ_ASSERT(aSource != Source::None && !aVendor.IsEmpty());
+ }
+
+ Source mSource;
+ nsString mVendor;
+ bool mHasNestedMicrosoftSignature;
+};
+
+class ModulesMap;
+
+class ModuleRecord final {
+ public:
+ explicit ModuleRecord(const nsAString& aResolvedNtPath);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ModuleRecord)
+
+ nsString mResolvedNtName;
+ nsCOMPtr<nsIFile> mResolvedDosName;
+ nsString mSanitizedDllName;
+ Maybe<ModuleVersion> mVersion;
+ Maybe<VendorInfo> mVendorInfo;
+ ModuleTrustFlags mTrustFlags;
+
+ explicit operator bool() const { return !mSanitizedDllName.IsEmpty(); }
+ bool IsXUL() const;
+ bool IsTrusted() const;
+
+ ModuleRecord(const ModuleRecord&) = delete;
+ ModuleRecord(ModuleRecord&&) = delete;
+
+ ModuleRecord& operator=(const ModuleRecord&) = delete;
+ ModuleRecord& operator=(ModuleRecord&&) = delete;
+
+ private:
+ ModuleRecord();
+ ~ModuleRecord() = default;
+ void GetVersionAndVendorInfo(const nsAString& aPath);
+ int32_t GetScoreThreshold() const;
+
+ friend struct ::IPC::ParamTraits<ModulesMap>;
+};
+
+/**
+ * This type holds module path data using one of two internal representations.
+ * It may be created from either a nsTHashtable or a Vector, and may be
+ * serialized from either representation into a common format over the wire.
+ * Deserialization always uses the Vector representation.
+ */
+struct ModulePaths final {
+ using SetType = nsTHashtable<nsStringCaseInsensitiveHashKey>;
+ using VecType = Vector<nsString>;
+
+ Variant<SetType, VecType> mModuleNtPaths;
+
+ template <typename T>
+ explicit ModulePaths(T&& aPaths)
+ : mModuleNtPaths(AsVariant(std::forward<T>(aPaths))) {}
+
+ ModulePaths() : mModuleNtPaths(VecType()) {}
+
+ ModulePaths(const ModulePaths& aOther) = delete;
+ ModulePaths(ModulePaths&& aOther) = default;
+ ModulePaths& operator=(const ModulePaths&) = delete;
+ ModulePaths& operator=(ModulePaths&&) = default;
+};
+
+class ProcessedModuleLoadEvent final {
+ public:
+ ProcessedModuleLoadEvent();
+ ProcessedModuleLoadEvent(glue::EnhancedModuleLoadInfo&& aModLoadInfo,
+ RefPtr<ModuleRecord>&& aModuleRecord);
+
+ explicit operator bool() const { return mModule && *mModule; }
+ bool IsXULLoad() const;
+ bool IsTrusted() const;
+
+ uint64_t mProcessUptimeMS;
+ Maybe<double> mLoadDurationMS;
+ DWORD mThreadId;
+ nsCString mThreadName;
+ nsString mRequestedDllName;
+ // We intentionally store mBaseAddress as part of the event and not the
+ // module, as relocation may cause it to change between loads. If so, we want
+ // to know about it.
+ uintptr_t mBaseAddress;
+ RefPtr<ModuleRecord> mModule;
+ bool mIsDependent;
+ uint32_t mLoadStatus; // corresponding to enum ModuleLoadInfo::Status
+
+ ProcessedModuleLoadEvent(const ProcessedModuleLoadEvent&) = delete;
+ ProcessedModuleLoadEvent& operator=(const ProcessedModuleLoadEvent&) = delete;
+
+ ProcessedModuleLoadEvent(ProcessedModuleLoadEvent&&) = default;
+ ProcessedModuleLoadEvent& operator=(ProcessedModuleLoadEvent&&) = default;
+
+ void SanitizeRequestedDllName();
+
+ private:
+ static Maybe<LONGLONG> ComputeQPCTimeStampForProcessCreation();
+ static uint64_t QPCTimeStampToProcessUptimeMilliseconds(
+ const LARGE_INTEGER& aTimeStamp);
+};
+
+// Declaring ModulesMap this way makes it much easier to forward declare than
+// if we had used |using| or |typedef|.
+class ModulesMap final
+ : public nsRefPtrHashtable<nsStringCaseInsensitiveHashKey, ModuleRecord> {
+ public:
+ ModulesMap()
+ : nsRefPtrHashtable<nsStringCaseInsensitiveHashKey, ModuleRecord>() {}
+};
+
+struct ProcessedModuleLoadEventContainer final
+ : public LinkedListElement<ProcessedModuleLoadEventContainer> {
+ ProcessedModuleLoadEvent mEvent;
+ ProcessedModuleLoadEventContainer() = default;
+ explicit ProcessedModuleLoadEventContainer(ProcessedModuleLoadEvent&& aEvent)
+ : mEvent(std::move(aEvent)) {}
+
+ ProcessedModuleLoadEventContainer(ProcessedModuleLoadEventContainer&&) =
+ default;
+ ProcessedModuleLoadEventContainer& operator=(
+ ProcessedModuleLoadEventContainer&&) = default;
+ ProcessedModuleLoadEventContainer(const ProcessedModuleLoadEventContainer&) =
+ delete;
+ ProcessedModuleLoadEventContainer& operator=(
+ const ProcessedModuleLoadEventContainer&) = delete;
+};
+using UntrustedModuleLoadingEvents =
+ AutoCleanLinkedList<ProcessedModuleLoadEventContainer>;
+
+class UntrustedModulesData final {
+ // Merge aNewData.mEvents into this->mModules and also
+ // make module entries in aNewData point to items in this->mModules.
+ void MergeModules(UntrustedModulesData& aNewData);
+
+ public:
+ // Ensure mEvents will never retain more than kMaxEvents events.
+ // This constant matches the maximum in Telemetry::CombinedStacks.
+ // Truncate() relies on these being the same.
+ static constexpr size_t kMaxEvents = 50;
+
+ UntrustedModulesData()
+ : mProcessType(XRE_GetProcessType()),
+ mPid(::GetCurrentProcessId()),
+ mNumEvents(0),
+ mSanitizationFailures(0),
+ mTrustTestFailures(0) {
+ MOZ_ASSERT(kMaxEvents == mStacks.GetMaxStacksCount());
+ }
+
+ UntrustedModulesData(UntrustedModulesData&&) = default;
+ UntrustedModulesData& operator=(UntrustedModulesData&&) = default;
+
+ UntrustedModulesData(const UntrustedModulesData&) = delete;
+ UntrustedModulesData& operator=(const UntrustedModulesData&) = delete;
+
+ explicit operator bool() const {
+ return !mEvents.isEmpty() || mSanitizationFailures || mTrustTestFailures ||
+ mXULLoadDurationMS.isSome();
+ }
+
+ void AddNewLoads(const ModulesMap& aModulesMap,
+ UntrustedModuleLoadingEvents&& aEvents,
+ Vector<Telemetry::ProcessedStack>&& aStacks);
+ void Merge(UntrustedModulesData&& aNewData);
+ void MergeWithoutStacks(UntrustedModulesData&& aNewData);
+ void Swap(UntrustedModulesData& aOther);
+
+ // Drop callstack data and old loading events.
+ void Truncate(bool aDropCallstackData);
+
+ GeckoProcessType mProcessType;
+ DWORD mPid;
+ TimeDuration mElapsed;
+ ModulesMap mModules;
+ uint32_t mNumEvents;
+ UntrustedModuleLoadingEvents mEvents;
+ Telemetry::CombinedStacks mStacks;
+ Maybe<double> mXULLoadDurationMS;
+ uint32_t mSanitizationFailures;
+ uint32_t mTrustTestFailures;
+};
+
+class ModulesMapResult final {
+ public:
+ ModulesMapResult() : mTrustTestFailures(0) {}
+
+ ModulesMapResult(const ModulesMapResult& aOther) = delete;
+ ModulesMapResult(ModulesMapResult&& aOther) = default;
+ ModulesMapResult& operator=(const ModulesMapResult& aOther) = delete;
+ ModulesMapResult& operator=(ModulesMapResult&& aOther) = default;
+
+ ModulesMap mModules;
+ uint32_t mTrustTestFailures;
+};
+
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::ModuleVersion> {
+ typedef mozilla::ModuleVersion paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aWriter->WriteUInt64(aParam.AsInteger());
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ uint64_t ver;
+ if (!aReader->ReadUInt64(&ver)) {
+ return false;
+ }
+
+ *aResult = ver;
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::VendorInfo> {
+ typedef mozilla::VendorInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aWriter->WriteUInt32(static_cast<uint32_t>(aParam.mSource));
+ WriteParam(aWriter, aParam.mVendor);
+ WriteParam(aWriter, aParam.mHasNestedMicrosoftSignature);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ uint32_t source;
+ if (!aReader->ReadUInt32(&source)) {
+ return false;
+ }
+
+ aResult->mSource = static_cast<mozilla::VendorInfo::Source>(source);
+
+ if (!ReadParam(aReader, &aResult->mVendor)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mHasNestedMicrosoftSignature)) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ModuleRecord> {
+ typedef mozilla::ModuleRecord paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mResolvedNtName);
+
+ nsAutoString resolvedDosName;
+ if (aParam.mResolvedDosName) {
+ mozilla::DebugOnly<nsresult> rv =
+ aParam.mResolvedDosName->GetPath(resolvedDosName);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ WriteParam(aWriter, resolvedDosName);
+ WriteParam(aWriter, aParam.mSanitizedDllName);
+ WriteParam(aWriter, aParam.mVersion);
+ WriteParam(aWriter, aParam.mVendorInfo);
+ aWriter->WriteUInt32(static_cast<uint32_t>(aParam.mTrustFlags));
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &aResult->mResolvedNtName)) {
+ return false;
+ }
+
+ nsAutoString resolvedDosName;
+ if (!ReadParam(aReader, &resolvedDosName)) {
+ return false;
+ }
+
+ if (resolvedDosName.IsEmpty()) {
+ aResult->mResolvedDosName = nullptr;
+ } else if (NS_FAILED(NS_NewLocalFile(
+ resolvedDosName, false,
+ getter_AddRefs(aResult->mResolvedDosName)))) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mSanitizedDllName)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mVersion)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mVendorInfo)) {
+ return false;
+ }
+
+ uint32_t trustFlags;
+ if (!aReader->ReadUInt32(&trustFlags)) {
+ return false;
+ }
+
+ aResult->mTrustFlags = static_cast<mozilla::ModuleTrustFlags>(trustFlags);
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ModulesMap> {
+ typedef mozilla::ModulesMap paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aWriter->WriteUInt32(aParam.Count());
+
+ for (const auto& entry : aParam) {
+ MOZ_RELEASE_ASSERT(entry.GetData());
+ WriteParam(aWriter, entry.GetKey());
+ WriteParam(aWriter, *(entry.GetData()));
+ }
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ uint32_t count;
+ if (!ReadParam(aReader, &count)) {
+ return false;
+ }
+
+ for (uint32_t current = 0; current < count; ++current) {
+ nsAutoString key;
+ if (!ReadParam(aReader, &key) || key.IsEmpty()) {
+ return false;
+ }
+
+ RefPtr<mozilla::ModuleRecord> rec(new mozilla::ModuleRecord());
+ if (!ReadParam(aReader, rec.get())) {
+ return false;
+ }
+
+ aResult->InsertOrUpdate(key, std::move(rec));
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ModulePaths> {
+ typedef mozilla::ModulePaths paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aParam.mModuleNtPaths.match(
+ [aWriter](const paramType::SetType& aSet) { WriteSet(aWriter, aSet); },
+ [aWriter](const paramType::VecType& aVec) {
+ WriteVector(aWriter, aVec);
+ });
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ uint32_t len;
+ if (!aReader->ReadUInt32(&len)) {
+ return false;
+ }
+
+ // As noted in the comments for ModulePaths, we only deserialize using the
+ // Vector representation.
+ auto& vec = aResult->mModuleNtPaths.as<paramType::VecType>();
+ if (!vec.reserve(len)) {
+ return false;
+ }
+
+ for (uint32_t idx = 0; idx < len; ++idx) {
+ nsString str;
+ if (!ReadParam(aReader, &str)) {
+ return false;
+ }
+
+ if (!vec.emplaceBack(std::move(str))) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ // NB: This function must write out the set in the same format as WriteVector
+ static void WriteSet(MessageWriter* aWriter, const paramType::SetType& aSet) {
+ aWriter->WriteUInt32(aSet.Count());
+ for (const auto& key : aSet.Keys()) {
+ WriteParam(aWriter, key);
+ }
+ }
+
+ // NB: This function must write out the vector in the same format as WriteSet
+ static void WriteVector(MessageWriter* aWriter,
+ const paramType::VecType& aVec) {
+ aWriter->WriteUInt32(aVec.length());
+ for (auto const& item : aVec) {
+ WriteParam(aWriter, item);
+ }
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::UntrustedModulesData> {
+ typedef mozilla::UntrustedModulesData paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aWriter->WriteUInt32(aParam.mProcessType);
+ aWriter->WriteULong(aParam.mPid);
+ WriteParam(aWriter, aParam.mElapsed);
+ WriteParam(aWriter, aParam.mModules);
+
+ aWriter->WriteUInt32(aParam.mNumEvents);
+ for (auto event : aParam.mEvents) {
+ WriteEvent(aWriter, event->mEvent);
+ }
+
+ WriteParam(aWriter, aParam.mStacks);
+ WriteParam(aWriter, aParam.mXULLoadDurationMS);
+ aWriter->WriteUInt32(aParam.mSanitizationFailures);
+ aWriter->WriteUInt32(aParam.mTrustTestFailures);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ uint32_t processType;
+ if (!aReader->ReadUInt32(&processType)) {
+ return false;
+ }
+
+ aResult->mProcessType = static_cast<GeckoProcessType>(processType);
+
+ if (!aReader->ReadULong(&aResult->mPid)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mElapsed)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mModules)) {
+ return false;
+ }
+
+ // We read mEvents manually so that we can use ReadEvent defined below.
+ if (!ReadParam(aReader, &aResult->mNumEvents)) {
+ return false;
+ }
+
+ for (uint32_t curEventIdx = 0; curEventIdx < aResult->mNumEvents;
+ ++curEventIdx) {
+ auto newEvent =
+ mozilla::MakeUnique<mozilla::ProcessedModuleLoadEventContainer>();
+ if (!ReadEvent(aReader, &newEvent->mEvent, aResult->mModules)) {
+ return false;
+ }
+ aResult->mEvents.insertBack(newEvent.release());
+ }
+
+ if (!ReadParam(aReader, &aResult->mStacks)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mXULLoadDurationMS)) {
+ return false;
+ }
+
+ if (!aReader->ReadUInt32(&aResult->mSanitizationFailures)) {
+ return false;
+ }
+
+ if (!aReader->ReadUInt32(&aResult->mTrustTestFailures)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ // Because ProcessedModuleLoadEvent depends on a hash table from
+ // UntrustedModulesData, we do its serialization as part of this
+ // specialization.
+ static void WriteEvent(MessageWriter* aWriter,
+ const mozilla::ProcessedModuleLoadEvent& aParam) {
+ aWriter->WriteUInt64(aParam.mProcessUptimeMS);
+ WriteParam(aWriter, aParam.mLoadDurationMS);
+ aWriter->WriteULong(aParam.mThreadId);
+ WriteParam(aWriter, aParam.mThreadName);
+ WriteParam(aWriter, aParam.mRequestedDllName);
+ WriteParam(aWriter, aParam.mBaseAddress);
+ WriteParam(aWriter, aParam.mIsDependent);
+ WriteParam(aWriter, aParam.mLoadStatus);
+
+ // We don't write the ModuleRecord directly; we write its key into the
+ // UntrustedModulesData::mModules hash table.
+ MOZ_ASSERT(aParam.mModule && !aParam.mModule->mResolvedNtName.IsEmpty());
+ WriteParam(aWriter, aParam.mModule->mResolvedNtName);
+ }
+
+ // Because ProcessedModuleLoadEvent depends on a hash table from
+ // UntrustedModulesData, we do its deserialization as part of this
+ // specialization.
+ static bool ReadEvent(MessageReader* aReader,
+ mozilla::ProcessedModuleLoadEvent* aResult,
+ const mozilla::ModulesMap& aModulesMap) {
+ if (!aReader->ReadUInt64(&aResult->mProcessUptimeMS)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mLoadDurationMS)) {
+ return false;
+ }
+
+ if (!aReader->ReadULong(&aResult->mThreadId)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mThreadName)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mRequestedDllName)) {
+ return false;
+ }
+
+ // When ProcessedModuleLoadEvent was constructed in a child process, we left
+ // mRequestedDllName unsanitized, so now is a good time to sanitize it.
+ aResult->SanitizeRequestedDllName();
+
+ if (!ReadParam(aReader, &aResult->mBaseAddress)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mIsDependent)) {
+ return false;
+ }
+
+ if (!ReadParam(aReader, &aResult->mLoadStatus)) {
+ return false;
+ }
+
+ nsAutoString resolvedNtName;
+ if (!ReadParam(aReader, &resolvedNtName)) {
+ return false;
+ }
+
+ // NB: While bad data integrity might for some reason result in a null
+ // mModule, we do not fail the deserialization; this is a data error,
+ // rather than an IPC error. The error is detected and dealt with in
+ // telemetry.
+ aResult->mModule = aModulesMap.Get(resolvedNtName);
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ModulesMapResult> {
+ typedef mozilla::ModulesMapResult paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mModules);
+ aWriter->WriteUInt32(aParam.mTrustTestFailures);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &aResult->mModules)) {
+ return false;
+ }
+
+ if (!aReader->ReadUInt32(&aResult->mTrustTestFailures)) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+} // namespace IPC
+
+#else // defined(XP_WIN)
+
+namespace mozilla {
+
+// For compiling IPDL on non-Windows platforms
+using UntrustedModulesData = uint32_t;
+using ModulePaths = uint32_t;
+using ModulesMapResult = uint32_t;
+
+} // namespace mozilla
+
+#endif // defined(XP_WIN)
+
+#endif // mozilla_UntrustedModulesData_h