summaryrefslogtreecommitdiffstats
path: root/mozglue/dllservices
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue/dllservices')
-rw-r--r--mozglue/dllservices/Authenticode.cpp432
-rw-r--r--mozglue/dllservices/Authenticode.h32
-rw-r--r--mozglue/dllservices/LoaderAPIInterfaces.h120
-rw-r--r--mozglue/dllservices/LoaderObserver.cpp165
-rw-r--r--mozglue/dllservices/LoaderObserver.h45
-rw-r--r--mozglue/dllservices/ModuleLoadFrame.cpp105
-rw-r--r--mozglue/dllservices/ModuleLoadFrame.h44
-rw-r--r--mozglue/dllservices/ModuleLoadInfo.h173
-rw-r--r--mozglue/dllservices/NtLoaderAPI.h23
-rw-r--r--mozglue/dllservices/WindowsDllBlocklist.cpp782
-rw-r--r--mozglue/dllservices/WindowsDllBlocklist.h57
-rw-r--r--mozglue/dllservices/WindowsDllBlocklistCommon.h118
-rw-r--r--mozglue/dllservices/WindowsDllBlocklistDefs.in284
-rw-r--r--mozglue/dllservices/WindowsDllServices.h211
-rw-r--r--mozglue/dllservices/WindowsFallbackLoaderAPI.cpp86
-rw-r--r--mozglue/dllservices/WindowsFallbackLoaderAPI.h38
-rw-r--r--mozglue/dllservices/gen_dll_blocklist_defs.py744
-rw-r--r--mozglue/dllservices/moz.build60
18 files changed, 3519 insertions, 0 deletions
diff --git a/mozglue/dllservices/Authenticode.cpp b/mozglue/dllservices/Authenticode.cpp
new file mode 100644
index 0000000000..55ce487f20
--- /dev/null
+++ b/mozglue/dllservices/Authenticode.cpp
@@ -0,0 +1,432 @@
+/* -*- 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/. */
+
+// We need Windows 8 functions and structures to be able to verify SHA-256.
+#if defined(_WIN32_WINNT)
+# undef _WIN32_WINNT
+# define _WIN32_WINNT _WIN32_WINNT_WIN8
+#endif // defined(_WIN32_WINNT)
+#if defined(NTDDI_VERSION)
+# undef NTDDI_VERSION
+# define NTDDI_VERSION NTDDI_WIN8
+#endif // defined(NTDDI_VERSION)
+
+#include "Authenticode.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/DynamicallyLinkedFunctionPtr.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsWindowsHelpers.h"
+
+#include <windows.h>
+#include <softpub.h>
+#include <wincrypt.h>
+#include <wintrust.h>
+#include <mscat.h>
+
+#include <string.h>
+
+namespace {
+
+struct CertStoreDeleter {
+ typedef HCERTSTORE pointer;
+ void operator()(pointer aStore) { ::CertCloseStore(aStore, 0); }
+};
+
+struct CryptMsgDeleter {
+ typedef HCRYPTMSG pointer;
+ void operator()(pointer aMsg) { ::CryptMsgClose(aMsg); }
+};
+
+struct CertContextDeleter {
+ void operator()(PCCERT_CONTEXT aCertContext) {
+ ::CertFreeCertificateContext(aCertContext);
+ }
+};
+
+struct CATAdminContextDeleter {
+ typedef HCATADMIN pointer;
+ void operator()(pointer aCtx) {
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminReleaseContext)>
+ pCryptCATAdminReleaseContext(L"wintrust.dll",
+ "CryptCATAdminReleaseContext");
+
+ MOZ_ASSERT(!!pCryptCATAdminReleaseContext);
+ if (!pCryptCATAdminReleaseContext) {
+ return;
+ }
+
+ pCryptCATAdminReleaseContext(aCtx, 0);
+ }
+};
+
+typedef mozilla::UniquePtr<HCERTSTORE, CertStoreDeleter> CertStoreUniquePtr;
+typedef mozilla::UniquePtr<HCRYPTMSG, CryptMsgDeleter> CryptMsgUniquePtr;
+typedef mozilla::UniquePtr<const CERT_CONTEXT, CertContextDeleter>
+ CertContextUniquePtr;
+typedef mozilla::UniquePtr<HCATADMIN, CATAdminContextDeleter>
+ CATAdminContextUniquePtr;
+
+static const DWORD kEncodingTypes = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
+
+class SignedBinary final {
+ public:
+ SignedBinary(const wchar_t* aFilePath, mozilla::AuthenticodeFlags aFlags);
+
+ explicit operator bool() const { return mCertStore && mCryptMsg && mCertCtx; }
+
+ mozilla::UniquePtr<wchar_t[]> GetOrgName();
+
+ SignedBinary(const SignedBinary&) = delete;
+ SignedBinary(SignedBinary&&) = delete;
+ SignedBinary& operator=(const SignedBinary&) = delete;
+ SignedBinary& operator=(SignedBinary&&) = delete;
+
+ private:
+ bool VerifySignature(const wchar_t* aFilePath);
+ bool QueryObject(const wchar_t* aFilePath);
+ static bool VerifySignatureInternal(WINTRUST_DATA& aTrustData);
+
+ private:
+ enum class TrustSource { eNone, eEmbedded, eCatalog };
+
+ private:
+ const mozilla::AuthenticodeFlags mFlags;
+ TrustSource mTrustSource;
+ CertStoreUniquePtr mCertStore;
+ CryptMsgUniquePtr mCryptMsg;
+ CertContextUniquePtr mCertCtx;
+};
+
+SignedBinary::SignedBinary(const wchar_t* aFilePath,
+ mozilla::AuthenticodeFlags aFlags)
+ : mFlags(aFlags), mTrustSource(TrustSource::eNone) {
+ if (!VerifySignature(aFilePath)) {
+ return;
+ }
+
+ DWORD certInfoLen = 0;
+ BOOL ok = CryptMsgGetParam(mCryptMsg.get(), CMSG_SIGNER_CERT_INFO_PARAM, 0,
+ nullptr, &certInfoLen);
+ if (!ok) {
+ return;
+ }
+
+ auto certInfoBuf = mozilla::MakeUnique<char[]>(certInfoLen);
+
+ ok = CryptMsgGetParam(mCryptMsg.get(), CMSG_SIGNER_CERT_INFO_PARAM, 0,
+ certInfoBuf.get(), &certInfoLen);
+ if (!ok) {
+ return;
+ }
+
+ auto certInfo = reinterpret_cast<CERT_INFO*>(certInfoBuf.get());
+
+ PCCERT_CONTEXT certCtx =
+ CertFindCertificateInStore(mCertStore.get(), kEncodingTypes, 0,
+ CERT_FIND_SUBJECT_CERT, certInfo, nullptr);
+ if (!certCtx) {
+ return;
+ }
+
+ mCertCtx.reset(certCtx);
+}
+
+bool SignedBinary::QueryObject(const wchar_t* aFilePath) {
+ DWORD encodingType, contentType, formatType;
+ HCERTSTORE rawCertStore;
+ HCRYPTMSG rawCryptMsg;
+ BOOL result = ::CryptQueryObject(CERT_QUERY_OBJECT_FILE, aFilePath,
+ CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
+ CERT_QUERY_FORMAT_FLAG_BINARY, 0,
+ &encodingType, &contentType, &formatType,
+ &rawCertStore, &rawCryptMsg, nullptr);
+ if (!result) {
+ return false;
+ }
+
+ mCertStore.reset(rawCertStore);
+ mCryptMsg.reset(rawCryptMsg);
+
+ return true;
+}
+
+/**
+ * @param aTrustData must be a WINTRUST_DATA structure that has been zeroed out
+ * and then populated at least with its |cbStruct|,
+ * |dwUnionChoice|, and appropriate union field. This function
+ * will then populate the remaining fields as appropriate.
+ */
+/* static */
+bool SignedBinary::VerifySignatureInternal(WINTRUST_DATA& aTrustData) {
+ aTrustData.dwUIChoice = WTD_UI_NONE;
+ aTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
+ aTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
+ aTrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;
+
+ const HWND hwnd = (HWND)INVALID_HANDLE_VALUE;
+ GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
+ LONG result = ::WinVerifyTrust(hwnd, &policyGUID, &aTrustData);
+
+ aTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
+ ::WinVerifyTrust(hwnd, &policyGUID, &aTrustData);
+
+ return result == ERROR_SUCCESS;
+}
+
+bool SignedBinary::VerifySignature(const wchar_t* aFilePath) {
+ // First, try the binary itself
+ if (QueryObject(aFilePath)) {
+ mTrustSource = TrustSource::eEmbedded;
+ if (mFlags & mozilla::AuthenticodeFlags::SkipTrustVerification) {
+ return true;
+ }
+
+ WINTRUST_FILE_INFO fileInfo = {sizeof(fileInfo)};
+ fileInfo.pcwszFilePath = aFilePath;
+
+ WINTRUST_DATA trustData = {sizeof(trustData)};
+ trustData.dwUnionChoice = WTD_CHOICE_FILE;
+ trustData.pFile = &fileInfo;
+
+ return VerifySignatureInternal(trustData);
+ }
+
+ // We didn't find anything in the binary, so now try a catalog file.
+
+ // First, we open a catalog admin context.
+ HCATADMIN rawCatAdmin;
+
+ // Windows 7 also exports the CryptCATAdminAcquireContext2 API, but it does
+ // *not* sign its binaries with SHA-256, so we use the old API in that case.
+ if (mozilla::IsWin8OrLater()) {
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminAcquireContext2)>
+ pCryptCATAdminAcquireContext2(L"wintrust.dll",
+ "CryptCATAdminAcquireContext2");
+ if (!pCryptCATAdminAcquireContext2) {
+ return false;
+ }
+
+ CERT_STRONG_SIGN_PARA policy = {sizeof(policy)};
+ policy.dwInfoChoice = CERT_STRONG_SIGN_OID_INFO_CHOICE;
+ policy.pszOID = const_cast<char*>(
+ szOID_CERT_STRONG_SIGN_OS_CURRENT); // -Wwritable-strings
+
+ if (!pCryptCATAdminAcquireContext2(&rawCatAdmin, nullptr,
+ BCRYPT_SHA256_ALGORITHM, &policy, 0)) {
+ return false;
+ }
+ } else {
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminAcquireContext)>
+ pCryptCATAdminAcquireContext(L"wintrust.dll",
+ "CryptCATAdminAcquireContext");
+
+ if (!pCryptCATAdminAcquireContext ||
+ !pCryptCATAdminAcquireContext(&rawCatAdmin, nullptr, 0)) {
+ return false;
+ }
+ }
+
+ CATAdminContextUniquePtr catAdmin(rawCatAdmin);
+
+ // Now we need to hash the file at aFilePath.
+ // Since we're hashing this file, let's open it with a sequential scan hint.
+ HANDLE rawFile =
+ ::CreateFileW(aFilePath, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
+ nullptr, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
+ if (rawFile == INVALID_HANDLE_VALUE) {
+ return false;
+ }
+
+ nsAutoHandle file(rawFile);
+ DWORD hashLen = 0;
+ mozilla::UniquePtr<BYTE[]> hashBuf;
+
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminCalcHashFromFileHandle2)>
+ pCryptCATAdminCalcHashFromFileHandle2(
+ L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle2");
+ if (pCryptCATAdminCalcHashFromFileHandle2) {
+ if (!pCryptCATAdminCalcHashFromFileHandle2(rawCatAdmin, rawFile, &hashLen,
+ nullptr, 0) &&
+ ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+
+ hashBuf = mozilla::MakeUnique<BYTE[]>(hashLen);
+
+ if (!pCryptCATAdminCalcHashFromFileHandle2(rawCatAdmin, rawFile, &hashLen,
+ hashBuf.get(), 0)) {
+ return false;
+ }
+ } else {
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminCalcHashFromFileHandle)>
+ pCryptCATAdminCalcHashFromFileHandle(
+ L"wintrust.dll", "CryptCATAdminCalcHashFromFileHandle");
+
+ if (!pCryptCATAdminCalcHashFromFileHandle) {
+ return false;
+ }
+
+ if (!pCryptCATAdminCalcHashFromFileHandle(rawFile, &hashLen, nullptr, 0) &&
+ ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+
+ hashBuf = mozilla::MakeUnique<BYTE[]>(hashLen);
+
+ if (!pCryptCATAdminCalcHashFromFileHandle(rawFile, &hashLen, hashBuf.get(),
+ 0)) {
+ return false;
+ }
+ }
+
+ // Now that we've hashed the file, query the catalog system to see if any
+ // catalogs reference a binary with our hash.
+
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminEnumCatalogFromHash)>
+ pCryptCATAdminEnumCatalogFromHash(L"wintrust.dll",
+ "CryptCATAdminEnumCatalogFromHash");
+ if (!pCryptCATAdminEnumCatalogFromHash) {
+ return false;
+ }
+
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATAdminReleaseCatalogContext)>
+ pCryptCATAdminReleaseCatalogContext(L"wintrust.dll",
+ "CryptCATAdminReleaseCatalogContext");
+ if (!pCryptCATAdminReleaseCatalogContext) {
+ return false;
+ }
+
+ HCATINFO catInfoHdl = pCryptCATAdminEnumCatalogFromHash(
+ rawCatAdmin, hashBuf.get(), hashLen, 0, nullptr);
+ if (!catInfoHdl) {
+ return false;
+ }
+
+ // We can't use UniquePtr for this because the deleter function requires two
+ // parameters.
+ auto cleanCatInfoHdl =
+ mozilla::MakeScopeExit([rawCatAdmin, catInfoHdl]() -> void {
+ pCryptCATAdminReleaseCatalogContext(rawCatAdmin, catInfoHdl, 0);
+ });
+
+ // We found a catalog! Now query for the path to the catalog file.
+
+ static const mozilla::StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::CryptCATCatalogInfoFromContext)>
+ pCryptCATCatalogInfoFromContext(L"wintrust.dll",
+ "CryptCATCatalogInfoFromContext");
+ if (!pCryptCATCatalogInfoFromContext) {
+ return false;
+ }
+
+ CATALOG_INFO_ catInfo = {sizeof(catInfo)};
+ if (!pCryptCATCatalogInfoFromContext(catInfoHdl, &catInfo, 0)) {
+ return false;
+ }
+
+ if (!QueryObject(catInfo.wszCatalogFile)) {
+ return false;
+ }
+
+ mTrustSource = TrustSource::eCatalog;
+
+ if (mFlags & mozilla::AuthenticodeFlags::SkipTrustVerification) {
+ return true;
+ }
+
+ // WINTRUST_CATALOG_INFO::pcwszMemberTag is commonly set to the string
+ // representation of the file hash, so we build that here.
+
+ DWORD strHashBufLen = (hashLen * 2) + 1;
+ auto strHashBuf = mozilla::MakeUnique<wchar_t[]>(strHashBufLen);
+ if (!::CryptBinaryToStringW(hashBuf.get(), hashLen,
+ CRYPT_STRING_HEXRAW | CRYPT_STRING_NOCRLF,
+ strHashBuf.get(), &strHashBufLen)) {
+ return false;
+ }
+
+ // Ensure that the tag is uppercase for WinVerifyTrust
+ // NB: CryptBinaryToStringW overwrites strHashBufLen with the length excluding
+ // the null terminator, so we need to add it back for this call.
+ if (_wcsupr_s(strHashBuf.get(), strHashBufLen + 1)) {
+ return false;
+ }
+
+ // Now, given the path to the catalog, and the path to the member (ie, the
+ // binary whose hash we are validating), we may now validate. If the
+ // validation is successful, we then QueryObject on the *catalog file*
+ // instead of the binary.
+
+ WINTRUST_CATALOG_INFO wtCatInfo = {sizeof(wtCatInfo)};
+ wtCatInfo.pcwszCatalogFilePath = catInfo.wszCatalogFile;
+ wtCatInfo.pcwszMemberTag = strHashBuf.get();
+ wtCatInfo.pcwszMemberFilePath = aFilePath;
+ wtCatInfo.hMemberFile = rawFile;
+ if (mozilla::IsWin8OrLater()) {
+ wtCatInfo.hCatAdmin = rawCatAdmin;
+ }
+
+ WINTRUST_DATA trustData = {sizeof(trustData)};
+ trustData.dwUnionChoice = WTD_CHOICE_CATALOG;
+ trustData.pCatalog = &wtCatInfo;
+
+ return VerifySignatureInternal(trustData);
+}
+
+mozilla::UniquePtr<wchar_t[]> SignedBinary::GetOrgName() {
+ DWORD charCount = CertGetNameStringW(
+ mCertCtx.get(), CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);
+ if (charCount <= 1) {
+ // Not found
+ return nullptr;
+ }
+
+ auto result = mozilla::MakeUnique<wchar_t[]>(charCount);
+ charCount = CertGetNameStringW(mCertCtx.get(), CERT_NAME_SIMPLE_DISPLAY_TYPE,
+ 0, nullptr, result.get(), charCount);
+ MOZ_ASSERT(charCount > 1);
+
+ return result;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+
+class AuthenticodeImpl : public Authenticode {
+ public:
+ virtual UniquePtr<wchar_t[]> GetBinaryOrgName(
+ const wchar_t* aFilePath,
+ AuthenticodeFlags aFlags = AuthenticodeFlags::Default) override;
+};
+
+UniquePtr<wchar_t[]> AuthenticodeImpl::GetBinaryOrgName(
+ const wchar_t* aFilePath, AuthenticodeFlags aFlags) {
+ SignedBinary bin(aFilePath, aFlags);
+ if (!bin) {
+ return nullptr;
+ }
+
+ return bin.GetOrgName();
+}
+
+static AuthenticodeImpl sAuthenticodeImpl;
+
+Authenticode* GetAuthenticode() { return &sAuthenticodeImpl; }
+
+} // namespace mozilla
diff --git a/mozglue/dllservices/Authenticode.h b/mozglue/dllservices/Authenticode.h
new file mode 100644
index 0000000000..182512da2c
--- /dev/null
+++ b/mozglue/dllservices/Authenticode.h
@@ -0,0 +1,32 @@
+/* -*- 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/. */
+
+#ifndef mozilla_Authenticode_h
+#define mozilla_Authenticode_h
+
+#include "mozilla/Maybe.h"
+#include "mozilla/TypedEnumBits.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+
+enum class AuthenticodeFlags : uint32_t {
+ Default = 0,
+ SkipTrustVerification = 1,
+};
+
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AuthenticodeFlags)
+
+class Authenticode {
+ public:
+ virtual UniquePtr<wchar_t[]> GetBinaryOrgName(
+ const wchar_t* aFilePath,
+ AuthenticodeFlags aFlags = AuthenticodeFlags::Default) = 0;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_Authenticode_h
diff --git a/mozglue/dllservices/LoaderAPIInterfaces.h b/mozglue/dllservices/LoaderAPIInterfaces.h
new file mode 100644
index 0000000000..4546cf79bd
--- /dev/null
+++ b/mozglue/dllservices/LoaderAPIInterfaces.h
@@ -0,0 +1,120 @@
+/* -*- 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_LoaderAPIInterfaces_h
+#define mozilla_LoaderAPIInterfaces_h
+
+#include "nscore.h"
+#include "mozilla/ModuleLoadInfo.h"
+
+namespace mozilla {
+namespace nt {
+
+class NS_NO_VTABLE LoaderObserver {
+ public:
+ /**
+ * Notification that a DLL load has begun.
+ *
+ * @param aContext Outparam that allows this observer to store any context
+ * information pertaining to the current load.
+ * @param aRequestedDllName The DLL name requested by whatever invoked the
+ * loader. This name may not match the effective
+ * name of the DLL once the loader has completed
+ * its path search.
+ */
+ virtual void OnBeginDllLoad(void** aContext,
+ PCUNICODE_STRING aRequestedDllName) = 0;
+
+ /**
+ * Query the observer to determine whether the DLL named |aLSPLeafName| needs
+ * to be substituted with another module, and substitute the module handle
+ * when necessary.
+ *
+ * @return true when substitution occurs, otherwise false
+ */
+ virtual bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) = 0;
+
+ /**
+ * Notification that a DLL load has ended.
+ *
+ * @param aContext The context that was set by the corresponding call to
+ * OnBeginDllLoad
+ * @param aNtStatus The NTSTATUS returned by LdrLoadDll
+ * @param aModuleLoadInfo Telemetry information that was gathered about the
+ * load.
+ */
+ virtual void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) = 0;
+
+ /**
+ * Called to inform the observer that it is no longer active and, if
+ * necessary, call aNext->OnForward() with any accumulated telemetry
+ * information.
+ */
+ virtual void Forward(LoaderObserver* aNext) = 0;
+
+ /**
+ * Receives a vector of module load telemetry from a previous LoaderObserver.
+ */
+ virtual void OnForward(ModuleLoadInfoVec&& aInfo) = 0;
+};
+
+class NS_NO_VTABLE LoaderAPI {
+ public:
+ /**
+ * Construct a new ModuleLoadInfo structure and notify the LoaderObserver
+ * that a library load is beginning.
+ */
+ virtual ModuleLoadInfo ConstructAndNotifyBeginDllLoad(
+ void** aContext, PCUNICODE_STRING aRequestedDllName) = 0;
+
+ /**
+ * Query to determine whether the DLL named |aLSPLeafName| needs to be
+ * substituted with another module, and substitute the module handle when
+ * necessary.
+ *
+ * @return true when substitution occurs, otherwise false
+ */
+ virtual bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) = 0;
+
+ /**
+ * Notification that a DLL load has ended.
+ */
+ virtual void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) = 0;
+
+ /**
+ * Given the address of a mapped section, obtain the name of the file that is
+ * backing it.
+ */
+ virtual AllocatedUnicodeString GetSectionName(void* aSectionAddr) = 0;
+
+ using InitDllBlocklistOOPFnPtr = LauncherVoidResultWithLineInfo (*)(
+ const wchar_t*, HANDLE, const IMAGE_THUNK_DATA*);
+ using HandleLauncherErrorFnPtr = void (*)(const LauncherError&, const char*);
+
+ /**
+ * Return a pointer to winlauncher's function.
+ * Used by sandboxBroker::LaunchApp.
+ */
+ virtual InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() = 0;
+ virtual HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() = 0;
+};
+
+struct WinLauncherFunctions final {
+ nt::LoaderAPI::InitDllBlocklistOOPFnPtr mInitDllBlocklistOOP;
+ nt::LoaderAPI::HandleLauncherErrorFnPtr mHandleLauncherError;
+
+ WinLauncherFunctions()
+ : mInitDllBlocklistOOP(nullptr), mHandleLauncherError(nullptr) {}
+};
+
+} // namespace nt
+} // namespace mozilla
+
+#endif // mozilla_LoaderAPIInterfaces_h
diff --git a/mozglue/dllservices/LoaderObserver.cpp b/mozglue/dllservices/LoaderObserver.cpp
new file mode 100644
index 0000000000..9c4fa53d3b
--- /dev/null
+++ b/mozglue/dllservices/LoaderObserver.cpp
@@ -0,0 +1,165 @@
+/* -*- 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 "LoaderObserver.h"
+
+#include "mozilla/AutoProfilerLabel.h"
+#include "mozilla/BaseProfilerMarkers.h"
+#include "mozilla/glue/WindowsUnicode.h"
+#include "mozilla/StackWalk_windows.h"
+
+namespace {
+
+struct LoadContext {
+ LoadContext(mozilla::ProfilerLabel&& aLabel,
+ mozilla::UniquePtr<char[]>&& aDynamicStringStorage)
+ : mProfilerLabel(std::move(aLabel)),
+ mDynamicStringStorage(std::move(aDynamicStringStorage)),
+ mStartTime(mozilla::TimeStamp::Now()) {}
+ mozilla::ProfilerLabel mProfilerLabel;
+ mozilla::UniquePtr<char[]> mDynamicStringStorage;
+ mozilla::TimeStamp mStartTime;
+};
+
+} // anonymous namespace
+
+namespace mozilla {
+
+extern glue::Win32SRWLock gDllServicesLock;
+extern glue::detail::DllServicesBase* gDllServices;
+
+namespace glue {
+
+void LoaderObserver::OnBeginDllLoad(void** aContext,
+ PCUNICODE_STRING aRequestedDllName) {
+ MOZ_ASSERT(aContext);
+ if (IsProfilerPresent()) {
+ UniquePtr<char[]> utf8RequestedDllName(WideToUTF8(aRequestedDllName));
+ const char* dynamicString = utf8RequestedDllName.get();
+ *aContext = new LoadContext(
+ ProfilerLabelBegin("mozilla::glue::LoaderObserver::OnBeginDllLoad",
+ dynamicString, &aContext),
+ std::move(utf8RequestedDllName));
+ }
+
+#ifdef _M_AMD64
+ // Prevent the stack walker from suspending this thread when LdrLoadDll
+ // holds the RtlLookupFunctionEntry lock.
+ SuppressStackWalking();
+#endif
+}
+
+bool LoaderObserver::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) {
+ // Currently unsupported
+ return false;
+}
+
+void LoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) {
+#ifdef _M_AMD64
+ DesuppressStackWalking();
+#endif
+
+ UniquePtr<LoadContext> loadContext(static_cast<LoadContext*>(aContext));
+ if (loadContext && IsValidProfilerLabel(loadContext->mProfilerLabel)) {
+ ProfilerLabelEnd(loadContext->mProfilerLabel);
+ BASE_PROFILER_MARKER_TEXT(
+ "DllLoad", OTHER,
+ MarkerTiming::IntervalUntilNowFrom(loadContext->mStartTime),
+ mozilla::ProfilerString8View::WrapNullTerminatedString(
+ loadContext->mDynamicStringStorage.get()));
+ }
+
+ // We want to record a denied DLL load regardless of |aNtStatus| because
+ // |aNtStatus| is set to access-denied when DLL load was blocked.
+ if ((!NT_SUCCESS(aNtStatus) && !aModuleLoadInfo.WasDenied()) ||
+ !aModuleLoadInfo.WasMapped()) {
+ return;
+ }
+
+ { // Scope for lock
+ AutoSharedLock lock(gDllServicesLock);
+ if (gDllServices) {
+ gDllServices->DispatchDllLoadNotification(std::move(aModuleLoadInfo));
+ return;
+ }
+ }
+
+ // No dll services, save for later
+ AutoExclusiveLock lock(mLock);
+ if (!mEnabled) {
+ return;
+ }
+
+ if (!mModuleLoads) {
+ mModuleLoads = new ModuleLoadInfoVec();
+ }
+
+ Unused << mModuleLoads->emplaceBack(
+ std::forward<ModuleLoadInfo>(aModuleLoadInfo));
+}
+
+void LoaderObserver::Forward(nt::LoaderObserver* aNext) {
+ MOZ_ASSERT_UNREACHABLE(
+ "This implementation does not forward to any more "
+ "nt::LoaderObserver objects");
+}
+
+void LoaderObserver::Forward(detail::DllServicesBase* aNext) {
+ MOZ_ASSERT(aNext);
+ if (!aNext) {
+ return;
+ }
+
+ ModuleLoadInfoVec* moduleLoads = nullptr;
+
+ { // Scope for lock
+ AutoExclusiveLock lock(mLock);
+ moduleLoads = mModuleLoads;
+ mModuleLoads = nullptr;
+ }
+
+ if (!moduleLoads) {
+ return;
+ }
+
+ aNext->DispatchModuleLoadBacklogNotification(std::move(*moduleLoads));
+ delete moduleLoads;
+}
+
+void LoaderObserver::Disable() {
+ ModuleLoadInfoVec* moduleLoads = nullptr;
+
+ { // Scope for lock
+ AutoExclusiveLock lock(mLock);
+ moduleLoads = mModuleLoads;
+ mModuleLoads = nullptr;
+ mEnabled = false;
+ }
+
+ delete moduleLoads;
+}
+
+void LoaderObserver::OnForward(ModuleLoadInfoVec&& aInfo) {
+ AutoExclusiveLock lock(mLock);
+ if (!mModuleLoads) {
+ mModuleLoads = new ModuleLoadInfoVec();
+ }
+
+ MOZ_ASSERT(mModuleLoads->empty());
+ if (mModuleLoads->empty()) {
+ *mModuleLoads = std::move(aInfo);
+ } else {
+ // This should not happen, but we can handle it
+ for (auto&& item : aInfo) {
+ Unused << mModuleLoads->append(std::move(item));
+ }
+ }
+}
+
+} // namespace glue
+} // namespace mozilla
diff --git a/mozglue/dllservices/LoaderObserver.h b/mozglue/dllservices/LoaderObserver.h
new file mode 100644
index 0000000000..39b13035a3
--- /dev/null
+++ b/mozglue/dllservices/LoaderObserver.h
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+#ifndef mozilla_glue_LoaderObserver_h
+#define mozilla_glue_LoaderObserver_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/LoaderAPIInterfaces.h"
+#include "mozilla/glue/WindowsDllServices.h"
+#include "mozilla/glue/WinUtils.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace glue {
+
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderObserver final
+ : public nt::LoaderObserver {
+ public:
+ constexpr LoaderObserver() : mModuleLoads(nullptr), mEnabled(true) {}
+
+ void OnBeginDllLoad(void** aContext,
+ PCUNICODE_STRING aPreliminaryDllName) final;
+ bool SubstituteForLSP(PCUNICODE_STRING aLspLeafName,
+ PHANDLE aOutHandle) final;
+ void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) final;
+ void Forward(nt::LoaderObserver* aNext) final;
+ void OnForward(ModuleLoadInfoVec&& aInfo) final;
+
+ void Forward(mozilla::glue::detail::DllServicesBase* aSvc);
+ void Disable();
+
+ private:
+ Win32SRWLock mLock;
+ ModuleLoadInfoVec* mModuleLoads;
+ bool mEnabled;
+};
+
+} // namespace glue
+} // namespace mozilla
+
+#endif // mozilla_glue_LoaderObserver_h
diff --git a/mozglue/dllservices/ModuleLoadFrame.cpp b/mozglue/dllservices/ModuleLoadFrame.cpp
new file mode 100644
index 0000000000..ff972836c3
--- /dev/null
+++ b/mozglue/dllservices/ModuleLoadFrame.cpp
@@ -0,0 +1,105 @@
+/* -*- 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 "ModuleLoadFrame.h"
+#include "mozilla/NativeNt.h"
+#include "mozilla/UniquePtr.h"
+#include "NtLoaderAPI.h"
+
+#include <string.h>
+
+#include "WindowsFallbackLoaderAPI.h"
+
+static bool IsNullTerminated(PCUNICODE_STRING aStr) {
+ return aStr && (aStr->MaximumLength >= (aStr->Length + sizeof(WCHAR))) &&
+ aStr->Buffer && aStr->Buffer[aStr->Length / sizeof(WCHAR)] == 0;
+}
+
+static mozilla::FallbackLoaderAPI gFallbackLoaderAPI;
+
+namespace mozilla {
+namespace glue {
+
+nt::LoaderAPI* ModuleLoadFrame::sLoaderAPI;
+
+using GetNtLoaderAPIFn = decltype(&mozilla::GetNtLoaderAPI);
+
+/* static */
+void ModuleLoadFrame::StaticInit(
+ nt::LoaderObserver* aNewObserver,
+ nt::WinLauncherFunctions* aOutWinLauncherFunctions) {
+ const auto pGetNtLoaderAPI = reinterpret_cast<GetNtLoaderAPIFn>(
+ ::GetProcAddress(::GetModuleHandleW(nullptr), "GetNtLoaderAPI"));
+ if (!pGetNtLoaderAPI) {
+ // This case occurs in processes other than firefox.exe that do not contain
+ // the launcher process blocklist.
+ gFallbackLoaderAPI.SetObserver(aNewObserver);
+ sLoaderAPI = &gFallbackLoaderAPI;
+
+ if (aOutWinLauncherFunctions) {
+ aOutWinLauncherFunctions->mHandleLauncherError =
+ [](const mozilla::LauncherError&, const char*) {};
+ // We intentionally leave mInitDllBlocklistOOP null to make sure calling
+ // mInitDllBlocklistOOP in non-Firefox hits MOZ_RELEASE_ASSERT.
+ }
+ return;
+ }
+
+ sLoaderAPI = pGetNtLoaderAPI(aNewObserver);
+ MOZ_ASSERT(sLoaderAPI);
+
+ if (aOutWinLauncherFunctions) {
+ aOutWinLauncherFunctions->mInitDllBlocklistOOP =
+ sLoaderAPI->GetDllBlocklistInitFn();
+ aOutWinLauncherFunctions->mHandleLauncherError =
+ sLoaderAPI->GetHandleLauncherErrorFn();
+ }
+}
+
+ModuleLoadFrame::ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName)
+ : mAlreadyLoaded(false),
+ mContext(nullptr),
+ mDllLoadStatus(STATUS_UNSUCCESSFUL),
+ mLoadInfo(sLoaderAPI->ConstructAndNotifyBeginDllLoad(&mContext,
+ aRequestedDllName)) {
+ if (!aRequestedDllName) {
+ return;
+ }
+
+ UniquePtr<WCHAR[]> nameBuf;
+ const WCHAR* name = nullptr;
+
+ if (IsNullTerminated(aRequestedDllName)) {
+ name = aRequestedDllName->Buffer;
+ } else {
+ USHORT charLenExclNul = aRequestedDllName->Length / sizeof(WCHAR);
+ USHORT charLenInclNul = charLenExclNul + 1;
+ nameBuf = MakeUnique<WCHAR[]>(charLenInclNul);
+ if (!wcsncpy_s(nameBuf.get(), charLenInclNul, aRequestedDllName->Buffer,
+ charLenExclNul)) {
+ name = nameBuf.get();
+ }
+ }
+
+ mAlreadyLoaded = name && !!::GetModuleHandleW(name);
+}
+
+ModuleLoadFrame::~ModuleLoadFrame() {
+ sLoaderAPI->NotifyEndDllLoad(mContext, mDllLoadStatus, std::move(mLoadInfo));
+}
+
+void ModuleLoadFrame::SetLoadStatus(NTSTATUS aNtStatus, HANDLE aHandle) {
+ mDllLoadStatus = aNtStatus;
+ void* baseAddr = mozilla::nt::PEHeaders::HModuleToBaseAddr<void*>(
+ reinterpret_cast<HMODULE>(aHandle));
+ mLoadInfo.mBaseAddr = baseAddr;
+ if (!mAlreadyLoaded) {
+ mLoadInfo.mSectionName = sLoaderAPI->GetSectionName(baseAddr);
+ }
+}
+
+} // namespace glue
+} // namespace mozilla
diff --git a/mozglue/dllservices/ModuleLoadFrame.h b/mozglue/dllservices/ModuleLoadFrame.h
new file mode 100644
index 0000000000..6f234f8788
--- /dev/null
+++ b/mozglue/dllservices/ModuleLoadFrame.h
@@ -0,0 +1,44 @@
+/* -*- 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/. */
+
+#ifndef mozilla_glue_ModuleLoadFrame_h
+#define mozilla_glue_ModuleLoadFrame_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/LoaderAPIInterfaces.h"
+
+namespace mozilla {
+namespace glue {
+
+class MOZ_RAII ModuleLoadFrame final {
+ public:
+ explicit ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName);
+ ~ModuleLoadFrame();
+
+ void SetLoadStatus(NTSTATUS aNtStatus, HANDLE aHandle);
+
+ ModuleLoadFrame(const ModuleLoadFrame&) = delete;
+ ModuleLoadFrame(ModuleLoadFrame&&) = delete;
+ ModuleLoadFrame& operator=(const ModuleLoadFrame&) = delete;
+ ModuleLoadFrame& operator=(ModuleLoadFrame&&) = delete;
+
+ static void StaticInit(nt::LoaderObserver* aNewObserver,
+ nt::WinLauncherFunctions* aOutWinLauncherFunctions);
+
+ private:
+ bool mAlreadyLoaded;
+ void* mContext;
+ NTSTATUS mDllLoadStatus;
+ ModuleLoadInfo mLoadInfo;
+
+ private:
+ static nt::LoaderAPI* sLoaderAPI;
+};
+
+} // namespace glue
+} // namespace mozilla
+
+#endif // mozilla_glue_ModuleLoadFrame_h
diff --git a/mozglue/dllservices/ModuleLoadInfo.h b/mozglue/dllservices/ModuleLoadInfo.h
new file mode 100644
index 0000000000..2d9bdc0cc7
--- /dev/null
+++ b/mozglue/dllservices/ModuleLoadInfo.h
@@ -0,0 +1,173 @@
+/* -*- 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_ModuleLoadInfo_h
+#define mozilla_ModuleLoadInfo_h
+
+#include "mozilla/NativeNt.h"
+#include "mozilla/Vector.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+
+struct ModuleLoadInfo final {
+ // If you add a new value or change the meaning of the values, please
+ // update createLoadStatusElement in aboutSupport.js accordingly, which
+ // defines text labels of these enum values displayed on about:support.
+ enum class Status : uint32_t {
+ Loaded = 0,
+ Blocked,
+ Redirected,
+ };
+
+ // We do not provide these methods inside Gecko proper.
+#if !defined(MOZILLA_INTERNAL_API)
+
+ /**
+ * This constructor is for use by the LdrLoadDll hook.
+ */
+ explicit ModuleLoadInfo(PCUNICODE_STRING aRequestedDllName)
+ : mLoadTimeInfo(),
+ mThreadId(nt::RtlGetCurrentThreadId()),
+ mRequestedDllName(aRequestedDllName),
+ mBaseAddr(nullptr),
+ mStatus(Status::Loaded) {
+# if defined(IMPL_MFBT)
+ ::QueryPerformanceCounter(&mBeginTimestamp);
+# else
+ ::RtlQueryPerformanceCounter(&mBeginTimestamp);
+# endif // defined(IMPL_MFBT)
+ }
+
+ /**
+ * This constructor is used by the NtMapViewOfSection hook IF AND ONLY IF
+ * the LdrLoadDll hook did not already construct a ModuleLoadInfo for the
+ * current DLL load. This may occur while the loader is loading dependencies
+ * of another library.
+ */
+ ModuleLoadInfo(nt::AllocatedUnicodeString&& aSectionName,
+ const void* aBaseAddr, Status aLoadStatus)
+ : mLoadTimeInfo(),
+ mThreadId(nt::RtlGetCurrentThreadId()),
+ mSectionName(std::move(aSectionName)),
+ mBaseAddr(aBaseAddr),
+ mStatus(aLoadStatus) {
+# if defined(IMPL_MFBT)
+ ::QueryPerformanceCounter(&mBeginTimestamp);
+# else
+ ::RtlQueryPerformanceCounter(&mBeginTimestamp);
+# endif // defined(IMPL_MFBT)
+ }
+
+ /**
+ * Marks the time that LdrLoadDll began loading this library.
+ */
+ void SetBeginLoadTimeStamp() {
+# if defined(IMPL_MFBT)
+ ::QueryPerformanceCounter(&mLoadTimeInfo);
+# else
+ ::RtlQueryPerformanceCounter(&mLoadTimeInfo);
+# endif // defined(IMPL_MFBT)
+ }
+
+ /**
+ * Marks the time that LdrLoadDll finished loading this library.
+ */
+ void SetEndLoadTimeStamp() {
+ LARGE_INTEGER endTimeStamp;
+# if defined(IMPL_MFBT)
+ ::QueryPerformanceCounter(&endTimeStamp);
+# else
+ ::RtlQueryPerformanceCounter(&endTimeStamp);
+# endif // defined(IMPL_MFBT)
+
+ LONGLONG& timeInfo = mLoadTimeInfo.QuadPart;
+ if (!timeInfo) {
+ return;
+ }
+
+ timeInfo = endTimeStamp.QuadPart - timeInfo;
+ }
+
+ /**
+ * Saves the current thread's call stack.
+ */
+ void CaptureBacktrace() {
+ const DWORD kMaxBacktraceSize = 512;
+
+ if (!mBacktrace.resize(kMaxBacktraceSize)) {
+ return;
+ }
+
+ // We don't use a Win32 variant here because Win32's CaptureStackBackTrace
+ // is just a macro that resolve to this function anyway.
+ WORD numCaptured = ::RtlCaptureStackBackTrace(2, kMaxBacktraceSize,
+ mBacktrace.begin(), nullptr);
+ Unused << mBacktrace.resize(numCaptured);
+ // These backtraces might stick around for a while, so let's trim any
+ // excess memory.
+ mBacktrace.shrinkStorageToFit();
+ }
+
+#endif // !defined(MOZILLA_INTERNAL_API)
+
+ ModuleLoadInfo(ModuleLoadInfo&&) = default;
+ ModuleLoadInfo& operator=(ModuleLoadInfo&&) = default;
+
+ ModuleLoadInfo() = delete;
+ ModuleLoadInfo(const ModuleLoadInfo&) = delete;
+ ModuleLoadInfo& operator=(const ModuleLoadInfo&) = delete;
+
+ /**
+ * A "bare" module load is one that was mapped without the code passing
+ * through a call to ntdll!LdrLoadDll.
+ */
+ bool IsBare() const {
+ // SetBeginLoadTimeStamp() and SetEndLoadTimeStamp() are only called by the
+ // LdrLoadDll hook, so when mLoadTimeInfo == 0, we know that we are bare.
+ return !mLoadTimeInfo.QuadPart;
+ }
+
+ /**
+ * Returns true for DLL loads where LdrLoadDll was called but
+ * NtMapViewOfSection was not. This will happen for DLL requests where the DLL
+ * was already mapped into memory by a previous request.
+ */
+ bool WasMapped() const { return !mSectionName.IsEmpty(); }
+
+ /**
+ * Returns true for DLL load which was denied by our blocklist.
+ */
+ bool WasDenied() const {
+ return mStatus == ModuleLoadInfo::Status::Blocked ||
+ mStatus == ModuleLoadInfo::Status::Redirected;
+ }
+
+ // Timestamp for the creation of this event
+ LARGE_INTEGER mBeginTimestamp;
+ // Duration of the LdrLoadDll call
+ LARGE_INTEGER mLoadTimeInfo;
+ // Thread ID of this DLL load
+ DWORD mThreadId;
+ // The name requested of LdrLoadDll by its caller
+ nt::AllocatedUnicodeString mRequestedDllName;
+ // The name of the DLL that backs section that was mapped by the loader. This
+ // string is the effective name of the DLL that was resolved by the loader's
+ // path search algorithm.
+ nt::AllocatedUnicodeString mSectionName;
+ // The base address of the module's mapped section
+ const void* mBaseAddr;
+ // If the module was successfully loaded, stack trace of the DLL load request
+ Vector<PVOID, 0, nt::RtlAllocPolicy> mBacktrace;
+ // The status of DLL load
+ Status mStatus;
+};
+
+using ModuleLoadInfoVec = Vector<ModuleLoadInfo, 0, nt::RtlAllocPolicy>;
+
+} // namespace mozilla
+
+#endif // mozilla_ModuleLoadInfo_h
diff --git a/mozglue/dllservices/NtLoaderAPI.h b/mozglue/dllservices/NtLoaderAPI.h
new file mode 100644
index 0000000000..628609092b
--- /dev/null
+++ b/mozglue/dllservices/NtLoaderAPI.h
@@ -0,0 +1,23 @@
+/* -*- 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_NtLoaderAPI_h
+#define mozilla_NtLoaderAPI_h
+
+#include "mozilla/LoaderAPIInterfaces.h"
+
+#if !defined(IMPL_MFBT)
+# error "This should only be included from mozglue!"
+#endif // !defined(IMPL_MFBT)
+
+namespace mozilla {
+
+extern "C" MOZ_IMPORT_API nt::LoaderAPI* GetNtLoaderAPI(
+ nt::LoaderObserver* aNewObserver);
+
+} // namespace mozilla
+
+#endif // mozilla_NtLoaderAPI_h
diff --git a/mozglue/dllservices/WindowsDllBlocklist.cpp b/mozglue/dllservices/WindowsDllBlocklist.cpp
new file mode 100644
index 0000000000..bacd6ad799
--- /dev/null
+++ b/mozglue/dllservices/WindowsDllBlocklist.cpp
@@ -0,0 +1,782 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 <windows.h>
+#include <winternl.h>
+
+#pragma warning(push)
+#pragma warning(disable : 4275 4530) // See msvc-stl-wrapper.template.h
+#include <map>
+#pragma warning(pop)
+
+#include "Authenticode.h"
+#include "BaseProfiler.h"
+#include "nsWindowsDllInterceptor.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/StackWalk_windows.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "nsWindowsHelpers.h"
+#include "WindowsDllBlocklist.h"
+#include "mozilla/AutoProfilerLabel.h"
+#include "mozilla/glue/Debug.h"
+#include "mozilla/glue/WindowsDllServices.h"
+#include "mozilla/glue/WinUtils.h"
+
+// Start new implementation
+#include "LoaderObserver.h"
+#include "ModuleLoadFrame.h"
+#include "mozilla/glue/WindowsUnicode.h"
+
+namespace mozilla {
+
+glue::Win32SRWLock gDllServicesLock;
+glue::detail::DllServicesBase* gDllServices;
+
+} // namespace mozilla
+
+using namespace mozilla;
+
+using CrashReporter::Annotation;
+using CrashReporter::AnnotationWriter;
+
+#define DLL_BLOCKLIST_ENTRY(name, ...) {name, __VA_ARGS__},
+#define DLL_BLOCKLIST_STRING_TYPE const char*
+#include "mozilla/WindowsDllBlocklistLegacyDefs.h"
+
+// define this for very verbose dll load debug spew
+#undef DEBUG_very_verbose
+
+static uint32_t sInitFlags;
+static bool sBlocklistInitAttempted;
+static bool sBlocklistInitFailed;
+static bool sUser32BeforeBlocklist;
+
+typedef MOZ_NORETURN_PTR void(__fastcall* BaseThreadInitThunk_func)(
+ BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
+static WindowsDllInterceptor::FuncHookType<BaseThreadInitThunk_func>
+ stub_BaseThreadInitThunk;
+
+typedef NTSTATUS(NTAPI* LdrLoadDll_func)(PWCHAR filePath, PULONG flags,
+ PUNICODE_STRING moduleFileName,
+ PHANDLE handle);
+static WindowsDllInterceptor::FuncHookType<LdrLoadDll_func> stub_LdrLoadDll;
+
+#ifdef _M_AMD64
+typedef decltype(
+ RtlInstallFunctionTableCallback)* RtlInstallFunctionTableCallback_func;
+static WindowsDllInterceptor::FuncHookType<RtlInstallFunctionTableCallback_func>
+ stub_RtlInstallFunctionTableCallback;
+
+extern uint8_t* sMsMpegJitCodeRegionStart;
+extern size_t sMsMpegJitCodeRegionSize;
+
+BOOLEAN WINAPI patched_RtlInstallFunctionTableCallback(
+ DWORD64 TableIdentifier, DWORD64 BaseAddress, DWORD Length,
+ PGET_RUNTIME_FUNCTION_CALLBACK Callback, PVOID Context,
+ PCWSTR OutOfProcessCallbackDll) {
+ // msmpeg2vdec.dll sets up a function table callback for their JIT code that
+ // just terminates the process, because their JIT doesn't have unwind info.
+ // If we see this callback being registered, record the region address, so
+ // that StackWalk.cpp can avoid unwinding addresses in this region.
+ //
+ // To keep things simple I'm not tracking unloads of msmpeg2vdec.dll.
+ // Worst case the stack walker will needlessly avoid a few pages of memory.
+
+ // Tricky: GetModuleHandleExW adds a ref by default; GetModuleHandleW doesn't.
+ HMODULE callbackModule = nullptr;
+ DWORD moduleFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
+
+ // These GetModuleHandle calls enter a critical section on Win7.
+ AutoSuppressStackWalking suppress;
+
+ if (GetModuleHandleExW(moduleFlags, (LPWSTR)Callback, &callbackModule) &&
+ GetModuleHandleW(L"msmpeg2vdec.dll") == callbackModule) {
+ sMsMpegJitCodeRegionStart = (uint8_t*)BaseAddress;
+ sMsMpegJitCodeRegionSize = Length;
+ }
+
+ return stub_RtlInstallFunctionTableCallback(TableIdentifier, BaseAddress,
+ Length, Callback, Context,
+ OutOfProcessCallbackDll);
+}
+#endif
+
+template <class T>
+struct RVAMap {
+ RVAMap(HANDLE map, DWORD offset) {
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+
+ DWORD alignedOffset =
+ (offset / info.dwAllocationGranularity) * info.dwAllocationGranularity;
+
+ MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf");
+
+ mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset,
+ sizeof(T) + (offset - alignedOffset));
+
+ mMappedView =
+ mRealView
+ ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset))
+ : nullptr;
+ }
+ ~RVAMap() {
+ if (mRealView) {
+ ::UnmapViewOfFile(mRealView);
+ }
+ }
+ operator const T*() const { return mMappedView; }
+ const T* operator->() const { return mMappedView; }
+
+ private:
+ const T* mMappedView;
+ void* mRealView;
+};
+
+static DWORD GetTimestamp(const wchar_t* path) {
+ DWORD timestamp = 0;
+
+ HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file != INVALID_HANDLE_VALUE) {
+ HANDLE map =
+ ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
+ if (map) {
+ RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
+ if (peHeader) {
+ RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
+ if (ntHeader) {
+ timestamp = ntHeader->FileHeader.TimeDateStamp;
+ }
+ }
+ ::CloseHandle(map);
+ }
+ ::CloseHandle(file);
+ }
+
+ return timestamp;
+}
+
+// This lock protects both the reentrancy sentinel and the crash reporter
+// data structures.
+static CRITICAL_SECTION sLock;
+
+/**
+ * Some versions of Windows call LoadLibraryEx to get the version information
+ * for a DLL, which causes our patched LdrLoadDll implementation to re-enter
+ * itself and cause infinite recursion and a stack-exhaustion crash. We protect
+ * against reentrancy by allowing recursive loads of the same DLL.
+ *
+ * Note that we don't use __declspec(thread) because that doesn't work in DLLs
+ * loaded via LoadLibrary and there can be a limited number of TLS slots, so
+ * we roll our own.
+ */
+class ReentrancySentinel {
+ public:
+ explicit ReentrancySentinel(const char* dllName) {
+ DWORD currentThreadId = GetCurrentThreadId();
+ AutoCriticalSection lock(&sLock);
+ mPreviousDllName = (*sThreadMap)[currentThreadId];
+
+ // If there is a DLL currently being loaded and it has the same name
+ // as the current attempt, we're re-entering.
+ mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName);
+ (*sThreadMap)[currentThreadId] = dllName;
+ }
+
+ ~ReentrancySentinel() {
+ DWORD currentThreadId = GetCurrentThreadId();
+ AutoCriticalSection lock(&sLock);
+ (*sThreadMap)[currentThreadId] = mPreviousDllName;
+ }
+
+ bool BailOut() const { return mReentered; };
+
+ static void InitializeStatics() {
+ InitializeCriticalSection(&sLock);
+ sThreadMap = new std::map<DWORD, const char*>;
+ }
+
+ private:
+ static std::map<DWORD, const char*>* sThreadMap;
+
+ const char* mPreviousDllName;
+ bool mReentered;
+};
+
+std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
+
+class WritableBuffer {
+ public:
+ WritableBuffer() : mBuffer{0}, mLen(0) {}
+
+ void Write(const char* aData, size_t aLen) {
+ size_t writable_len = std::min(aLen, Available());
+ memcpy(mBuffer + mLen, aData, writable_len);
+ mLen += writable_len;
+ }
+
+ size_t const Length() { return mLen; }
+ const char* Data() { return mBuffer; }
+
+ private:
+ size_t const Available() { return sizeof(mBuffer) - mLen; }
+
+ char mBuffer[1024];
+ size_t mLen;
+};
+
+/**
+ * This is a linked list of DLLs that have been blocked. It doesn't use
+ * mozilla::LinkedList because this is an append-only list and doesn't need
+ * to be doubly linked.
+ */
+class DllBlockSet {
+ public:
+ static void Add(const char* name, unsigned long long version);
+
+ // Write the list of blocked DLLs to a WritableBuffer object. This method is
+ // run after a crash occurs and must therefore not use the heap, etc.
+ static void Write(WritableBuffer& buffer);
+
+ private:
+ DllBlockSet(const char* name, unsigned long long version)
+ : mName(name), mVersion(version), mNext(nullptr) {}
+
+ const char* mName; // points into the gWindowsDllBlocklist string
+ unsigned long long mVersion;
+ DllBlockSet* mNext;
+
+ static DllBlockSet* gFirst;
+};
+
+DllBlockSet* DllBlockSet::gFirst;
+
+void DllBlockSet::Add(const char* name, unsigned long long version) {
+ AutoCriticalSection lock(&sLock);
+ for (DllBlockSet* b = gFirst; b; b = b->mNext) {
+ if (0 == strcmp(b->mName, name) && b->mVersion == version) {
+ return;
+ }
+ }
+ // Not already present
+ DllBlockSet* n = new DllBlockSet(name, version);
+ n->mNext = gFirst;
+ gFirst = n;
+}
+
+void DllBlockSet::Write(WritableBuffer& buffer) {
+ // It would be nicer to use AutoCriticalSection here. However, its destructor
+ // might not run if an exception occurs, in which case we would never leave
+ // the critical section. (MSVC warns about this possibility.) So we
+ // enter and leave manually.
+ ::EnterCriticalSection(&sLock);
+
+ // Because this method is called after a crash occurs, and uses heap memory,
+ // protect this entire block with a structured exception handler.
+ MOZ_SEH_TRY {
+ for (DllBlockSet* b = gFirst; b; b = b->mNext) {
+ // write name[,v.v.v.v];
+ buffer.Write(b->mName, strlen(b->mName));
+ if (b->mVersion != DllBlockInfo::ALL_VERSIONS) {
+ buffer.Write(",", 1);
+ uint16_t parts[4];
+ parts[0] = b->mVersion >> 48;
+ parts[1] = (b->mVersion >> 32) & 0xFFFF;
+ parts[2] = (b->mVersion >> 16) & 0xFFFF;
+ parts[3] = b->mVersion & 0xFFFF;
+ for (int p = 0; p < 4; ++p) {
+ char buf[32];
+ _ltoa_s(parts[p], buf, sizeof(buf), 10);
+ buffer.Write(buf, strlen(buf));
+ if (p != 3) {
+ buffer.Write(".", 1);
+ }
+ }
+ }
+ buffer.Write(";", 1);
+ }
+ }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {}
+
+ ::LeaveCriticalSection(&sLock);
+}
+
+static UniquePtr<wchar_t[]> getFullPath(PWCHAR filePath, wchar_t* fname) {
+ // In Windows 8, the first parameter seems to be used for more than just the
+ // path name. For example, its numerical value can be 1. Passing a non-valid
+ // pointer to SearchPathW will cause a crash, so we need to check to see if we
+ // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW.
+ PWCHAR sanitizedFilePath = nullptr;
+ if ((uintptr_t(filePath) >= 65536) && ((uintptr_t(filePath) & 1) == 0)) {
+ sanitizedFilePath = filePath;
+ }
+
+ // figure out the length of the string that we need
+ DWORD pathlen =
+ SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, nullptr);
+ if (pathlen == 0) {
+ return nullptr;
+ }
+
+ auto full_fname = MakeUnique<wchar_t[]>(pathlen + 1);
+ if (!full_fname) {
+ // couldn't allocate memory?
+ return nullptr;
+ }
+
+ // now actually grab it
+ SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname.get(),
+ nullptr);
+ return full_fname;
+}
+
+// No builtin function to find the last character matching a set
+static wchar_t* lastslash(wchar_t* s, int len) {
+ for (wchar_t* c = s + len - 1; c >= s; --c) {
+ if (*c == L'\\' || *c == L'/') {
+ return c;
+ }
+ }
+ return nullptr;
+}
+
+static NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR filePath, PULONG flags,
+ PUNICODE_STRING moduleFileName,
+ PHANDLE handle) {
+ // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename
+ // portion
+#define DLLNAME_MAX 128
+ char dllName[DLLNAME_MAX + 1];
+ wchar_t* dll_part;
+ char* dot;
+
+ int len = moduleFileName->Length / 2;
+ wchar_t* fname = moduleFileName->Buffer;
+ UniquePtr<wchar_t[]> full_fname;
+
+ // The filename isn't guaranteed to be null terminated, but in practice
+ // it always will be; ensure that this is so, and bail if not.
+ // This is done instead of the more robust approach because of bug 527122,
+ // where lots of weird things were happening when we tried to make a copy.
+ if (moduleFileName->MaximumLength < moduleFileName->Length + 2 ||
+ fname[len] != 0) {
+#ifdef DEBUG
+ printf_stderr("LdrLoadDll: non-null terminated string found!\n");
+#endif
+ goto continue_loading;
+ }
+
+ dll_part = lastslash(fname, len);
+ if (dll_part) {
+ dll_part = dll_part + 1;
+ len -= dll_part - fname;
+ } else {
+ dll_part = fname;
+ }
+
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len);
+#endif
+
+ // if it's too long, then, we assume we won't want to block it,
+ // since DLLNAME_MAX should be at least long enough to hold the longest
+ // entry in our blocklist.
+ if (len > DLLNAME_MAX) {
+#ifdef DEBUG
+ printf_stderr("LdrLoadDll: len too long! %d\n", len);
+#endif
+ goto continue_loading;
+ }
+
+ // copy over to our char byte buffer, lowercasing ASCII as we go
+ for (int i = 0; i < len; i++) {
+ wchar_t c = dll_part[i];
+
+ if (c > 0x7f) {
+ // welp, it's not ascii; if we need to add non-ascii things to
+ // our blocklist, we'll have to remove this limitation.
+ goto continue_loading;
+ }
+
+ // ensure that dll name is all lowercase
+ if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
+
+ dllName[i] = (char)c;
+ }
+
+ dllName[len] = 0;
+
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
+#endif
+
+ if (!(sInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
+ // Block a suspicious binary that uses various 12-digit hex strings
+ // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
+ dot = strchr(dllName, '.');
+ if (dot && (strchr(dot + 1, '.') == dot + 13)) {
+ char* end = nullptr;
+ _strtoui64(dot + 1, &end, 16);
+ if (end == dot + 13) {
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+ // Block binaries where the filename is at least 16 hex digits
+ if (dot && ((dot - dllName) >= 16)) {
+ char* current = dllName;
+ while (current < dot && isxdigit(*current)) {
+ current++;
+ }
+ if (current == dot) {
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+
+ // then compare to everything on the blocklist
+ DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info);
+ while (info->mName) {
+ if (strcmp(info->mName, dllName) == 0) break;
+
+ info++;
+ }
+
+ if (info->mName) {
+ bool load_ok = false;
+
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: info->mName: '%s'\n", info->mName);
+#endif
+
+ if (info->mFlags & DllBlockInfo::REDIRECT_TO_NOOP_ENTRYPOINT) {
+ printf_stderr(
+ "LdrLoadDll: "
+ "Ignoring the REDIRECT_TO_NOOP_ENTRYPOINT flag\n");
+ }
+
+ if ((info->mFlags & DllBlockInfo::BLOCK_WIN8_AND_OLDER) &&
+ IsWin8Point1OrLater()) {
+ goto continue_loading;
+ }
+
+ if ((info->mFlags & DllBlockInfo::BLOCK_WIN7_AND_OLDER) &&
+ IsWin8OrLater()) {
+ goto continue_loading;
+ }
+
+ if ((info->mFlags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
+ !(sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
+ goto continue_loading;
+ }
+
+ if ((info->mFlags & DllBlockInfo::BROWSER_PROCESS_ONLY) &&
+ (sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
+ goto continue_loading;
+ }
+
+ unsigned long long fVersion = DllBlockInfo::ALL_VERSIONS;
+
+ if (info->mMaxVersion != DllBlockInfo::ALL_VERSIONS) {
+ ReentrancySentinel sentinel(dllName);
+ if (sentinel.BailOut()) {
+ goto continue_loading;
+ }
+
+ full_fname = getFullPath(filePath, fname);
+ if (!full_fname) {
+ // uh, we couldn't find the DLL at all, so...
+ printf_stderr(
+ "LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find "
+ "it?)\n",
+ dllName);
+ return STATUS_DLL_NOT_FOUND;
+ }
+
+ if (info->mFlags & DllBlockInfo::USE_TIMESTAMP) {
+ fVersion = GetTimestamp(full_fname.get());
+ if (fVersion > info->mMaxVersion) {
+ load_ok = true;
+ }
+ } else {
+ LauncherResult<ModuleVersion> version =
+ GetModuleVersion(full_fname.get());
+ // If we failed to get the version information, we block.
+ if (version.isOk()) {
+ load_ok = !info->IsVersionBlocked(version.unwrap());
+ }
+ }
+ }
+
+ if (!load_ok) {
+ printf_stderr(
+ "LdrLoadDll: Blocking load of '%s' -- see "
+ "http://www.mozilla.com/en-US/blocklist/\n",
+ dllName);
+ DllBlockSet::Add(info->mName, fVersion);
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+ }
+
+continue_loading:
+#ifdef DEBUG_very_verbose
+ printf_stderr("LdrLoadDll: continuing load... ('%S')\n",
+ moduleFileName->Buffer);
+#endif
+
+ glue::ModuleLoadFrame loadFrame(moduleFileName);
+
+ NTSTATUS ret;
+ HANDLE myHandle;
+
+ ret = stub_LdrLoadDll(filePath, flags, moduleFileName, &myHandle);
+
+ if (handle) {
+ *handle = myHandle;
+ }
+
+ loadFrame.SetLoadStatus(ret, myHandle);
+
+ return ret;
+}
+
+#if defined(NIGHTLY_BUILD)
+// Map of specific thread proc addresses we should block. In particular,
+// LoadLibrary* APIs which indicate DLL injection
+static void* gStartAddressesToBlock[4];
+#endif // defined(NIGHTLY_BUILD)
+
+static bool ShouldBlockThread(void* aStartAddress) {
+ // Allows crashfirefox.exe to continue to work. Also if your threadproc is
+ // null, this crash is intentional.
+ if (aStartAddress == nullptr) return false;
+
+#if defined(NIGHTLY_BUILD)
+ for (auto p : gStartAddressesToBlock) {
+ if (p == aStartAddress) {
+ return true;
+ }
+ }
+#endif
+
+ bool shouldBlock = false;
+ MEMORY_BASIC_INFORMATION startAddressInfo = {0};
+ if (VirtualQuery(aStartAddress, &startAddressInfo,
+ sizeof(startAddressInfo))) {
+ shouldBlock |= startAddressInfo.State != MEM_COMMIT;
+ shouldBlock |= startAddressInfo.Protect != PAGE_EXECUTE_READ;
+ }
+
+ return shouldBlock;
+}
+
+// Allows blocked threads to still run normally through BaseThreadInitThunk, in
+// case there's any magic there that we shouldn't skip.
+static DWORD WINAPI NopThreadProc(void* /* aThreadParam */) { return 0; }
+
+static MOZ_NORETURN void __fastcall patched_BaseThreadInitThunk(
+ BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam) {
+ if (ShouldBlockThread(aStartAddress)) {
+ aStartAddress = (void*)NopThreadProc;
+ }
+
+ stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
+}
+
+static WindowsDllInterceptor NtDllIntercept;
+static WindowsDllInterceptor Kernel32Intercept;
+
+static void GetNativeNtBlockSetWriter();
+
+static glue::LoaderObserver gMozglueLoaderObserver;
+static nt::WinLauncherFunctions gWinLauncherFunctions;
+
+MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) {
+ if (sBlocklistInitAttempted) {
+ return;
+ }
+ sBlocklistInitAttempted = true;
+
+ sInitFlags = aInitFlags;
+
+ glue::ModuleLoadFrame::StaticInit(&gMozglueLoaderObserver,
+ &gWinLauncherFunctions);
+
+#ifdef _M_AMD64
+ if (!IsWin8OrLater()) {
+ Kernel32Intercept.Init("kernel32.dll");
+
+ // The crash that this hook works around is only seen on Win7.
+ stub_RtlInstallFunctionTableCallback.Set(
+ Kernel32Intercept, "RtlInstallFunctionTableCallback",
+ &patched_RtlInstallFunctionTableCallback);
+ }
+#endif
+
+ if (aInitFlags & eDllBlocklistInitFlagWasBootstrapped) {
+ GetNativeNtBlockSetWriter();
+ return;
+ }
+
+ // There are a couple of exceptional cases where we skip user32.dll check.
+ // - If the the process was bootstrapped by the launcher process, AppInit
+ // DLLs will be intercepted by the new DllBlockList. No need to check
+ // here.
+ // - The code to initialize the base profiler loads winmm.dll which
+ // statically links user32.dll on an older Windows. This means if the base
+ // profiler is active before coming here, we cannot fully intercept AppInit
+ // DLLs. Given that the base profiler is used outside the typical use
+ // cases, it's ok not to check user32.dll in this scenario.
+ const bool skipUser32Check =
+ (sInitFlags & eDllBlocklistInitFlagWasBootstrapped)
+#ifdef MOZ_GECKO_PROFILER
+ ||
+ (!IsWin10AnniversaryUpdateOrLater() && baseprofiler::profiler_is_active())
+#endif
+ ;
+
+ // In order to be effective against AppInit DLLs, the blocklist must be
+ // initialized before user32.dll is loaded into the process (bug 932100).
+ if (!skipUser32Check && GetModuleHandleW(L"user32.dll")) {
+ sUser32BeforeBlocklist = true;
+#ifdef DEBUG
+ printf_stderr("DLL blocklist was unable to intercept AppInit DLLs.\n");
+#endif
+ }
+
+ NtDllIntercept.Init("ntdll.dll");
+
+ ReentrancySentinel::InitializeStatics();
+
+ // We specifically use a detour, because there are cases where external
+ // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
+ // nop space patches. (Bug 951827)
+ bool ok = stub_LdrLoadDll.SetDetour(NtDllIntercept, "LdrLoadDll",
+ &patched_LdrLoadDll);
+
+ if (!ok) {
+ sBlocklistInitFailed = true;
+#ifdef DEBUG
+ printf_stderr("LdrLoadDll hook failed, no dll blocklisting active\n");
+#endif
+ }
+
+ // If someone injects a thread early that causes user32.dll to load off the
+ // main thread this causes issues, so load it as soon as we've initialized
+ // the block-list. (See bug 1400637)
+ if (!sUser32BeforeBlocklist) {
+ ::LoadLibraryW(L"user32.dll");
+ }
+
+ Kernel32Intercept.Init("kernel32.dll");
+
+ // Bug 1361410: WRusr.dll will overwrite our hook and cause a crash.
+ // Workaround: If we detect WRusr.dll, don't hook.
+ if (!GetModuleHandleW(L"WRusr.dll")) {
+ if (!stub_BaseThreadInitThunk.SetDetour(Kernel32Intercept,
+ "BaseThreadInitThunk",
+ &patched_BaseThreadInitThunk)) {
+#ifdef DEBUG
+ printf_stderr("BaseThreadInitThunk hook failed\n");
+#endif
+ }
+ }
+
+#if defined(NIGHTLY_BUILD)
+ // Populate a list of thread start addresses to block.
+ HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
+ if (hKernel) {
+ void* pProc;
+
+ pProc = (void*)GetProcAddress(hKernel, "LoadLibraryA");
+ gStartAddressesToBlock[0] = pProc;
+
+ pProc = (void*)GetProcAddress(hKernel, "LoadLibraryW");
+ gStartAddressesToBlock[1] = pProc;
+
+ pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExA");
+ gStartAddressesToBlock[2] = pProc;
+
+ pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExW");
+ gStartAddressesToBlock[3] = pProc;
+ }
+#endif
+}
+
+#ifdef DEBUG
+MFBT_API void DllBlocklist_Shutdown() {}
+#endif // DEBUG
+
+static void InternalWriteNotes(AnnotationWriter& aWriter) {
+ WritableBuffer buffer;
+ DllBlockSet::Write(buffer);
+
+ aWriter.Write(Annotation::BlockedDllList, buffer.Data(), buffer.Length());
+
+ if (sBlocklistInitFailed) {
+ aWriter.Write(Annotation::BlocklistInitFailed, "1");
+ }
+
+ if (sUser32BeforeBlocklist) {
+ aWriter.Write(Annotation::User32BeforeBlocklist, "1");
+ }
+}
+
+using WriterFn = void (*)(AnnotationWriter&);
+static WriterFn gWriterFn = &InternalWriteNotes;
+
+static void GetNativeNtBlockSetWriter() {
+ auto nativeWriter = reinterpret_cast<WriterFn>(
+ ::GetProcAddress(::GetModuleHandleW(nullptr), "NativeNtBlockSet_Write"));
+ if (nativeWriter) {
+ gWriterFn = nativeWriter;
+ }
+}
+
+MFBT_API void DllBlocklist_WriteNotes(AnnotationWriter& aWriter) {
+ MOZ_ASSERT(gWriterFn);
+ gWriterFn(aWriter);
+}
+
+MFBT_API bool DllBlocklist_CheckStatus() {
+ if (sBlocklistInitFailed || sUser32BeforeBlocklist) return false;
+ return true;
+}
+
+// ============================================================================
+// This section is for DLL Services
+// ============================================================================
+
+namespace mozilla {
+Authenticode* GetAuthenticode();
+} // namespace mozilla
+
+MFBT_API void DllBlocklist_SetFullDllServices(
+ mozilla::glue::detail::DllServicesBase* aSvc) {
+ glue::AutoExclusiveLock lock(gDllServicesLock);
+ if (aSvc) {
+ aSvc->SetAuthenticodeImpl(GetAuthenticode());
+ aSvc->SetWinLauncherFunctions(gWinLauncherFunctions);
+ gMozglueLoaderObserver.Forward(aSvc);
+ }
+
+ gDllServices = aSvc;
+}
+
+MFBT_API void DllBlocklist_SetBasicDllServices(
+ mozilla::glue::detail::DllServicesBase* aSvc) {
+ if (!aSvc) {
+ return;
+ }
+
+ aSvc->SetAuthenticodeImpl(GetAuthenticode());
+ gMozglueLoaderObserver.Disable();
+}
diff --git a/mozglue/dllservices/WindowsDllBlocklist.h b/mozglue/dllservices/WindowsDllBlocklist.h
new file mode 100644
index 0000000000..475c5a34a5
--- /dev/null
+++ b/mozglue/dllservices/WindowsDllBlocklist.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_windowsdllblocklist_h
+#define mozilla_windowsdllblocklist_h
+
+#if (defined(_MSC_VER) || defined(__MINGW32__)) && \
+ (defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64))
+
+# include <windows.h>
+# include "CrashAnnotations.h"
+# include "mozilla/Attributes.h"
+# include "mozilla/Types.h"
+
+# define HAS_DLL_BLOCKLIST
+
+enum DllBlocklistInitFlags {
+ eDllBlocklistInitFlagDefault = 0,
+ eDllBlocklistInitFlagIsChildProcess = 1,
+ eDllBlocklistInitFlagWasBootstrapped = 2
+};
+
+// Only available from within firefox.exe
+# if !defined(IMPL_MFBT) && !defined(MOZILLA_INTERNAL_API)
+extern uint32_t gBlocklistInitFlags;
+# endif // !defined(IMPL_MFBT) && !defined(MOZILLA_INTERNAL_API)
+
+MFBT_API void DllBlocklist_Initialize(
+ uint32_t aInitFlags = eDllBlocklistInitFlagDefault);
+MFBT_API void DllBlocklist_WriteNotes(CrashReporter::AnnotationWriter& aWriter);
+MFBT_API bool DllBlocklist_CheckStatus();
+
+// This export intends to clean up after DllBlocklist_Initialize().
+// It's disabled in release builds for performance and to limit callers' ability
+// to interfere with dll blocking.
+# ifdef DEBUG
+MFBT_API void DllBlocklist_Shutdown();
+# endif // DEBUG
+
+// Forward declaration
+namespace mozilla {
+namespace glue {
+namespace detail {
+class DllServicesBase;
+} // namespace detail
+} // namespace glue
+} // namespace mozilla
+
+MFBT_API void DllBlocklist_SetFullDllServices(
+ mozilla::glue::detail::DllServicesBase* aSvc);
+MFBT_API void DllBlocklist_SetBasicDllServices(
+ mozilla::glue::detail::DllServicesBase* aSvc);
+
+#endif // defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#endif // mozilla_windowsdllblocklist_h
diff --git a/mozglue/dllservices/WindowsDllBlocklistCommon.h b/mozglue/dllservices/WindowsDllBlocklistCommon.h
new file mode 100644
index 0000000000..aa8d65e135
--- /dev/null
+++ b/mozglue/dllservices/WindowsDllBlocklistCommon.h
@@ -0,0 +1,118 @@
+/* -*- 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_WindowsDllBlocklistCommon_h
+#define mozilla_WindowsDllBlocklistCommon_h
+
+#include <stdint.h>
+
+#include "mozilla/ArrayUtils.h"
+
+namespace mozilla {
+
+template <typename StrType>
+struct DllBlockInfoT {
+ // The name of the DLL -- in LOWERCASE! It will be compared to
+ // a lowercase version of the DLL name only.
+ StrType mName;
+
+ // If mMaxVersion is ALL_VERSIONS, we'll block all versions of this
+ // dll. Otherwise, we'll block all versions less than or equal to
+ // the given version, as queried by GetFileVersionInfo and
+ // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields.
+ //
+ // Note that the version is usually 4 components, which is A.B.C.D
+ // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity),
+ // but it's not required to be of that format.
+ uint64_t mMaxVersion;
+
+ // If the USE_TIMESTAMP flag is set, then we use the timestamp from
+ // the IMAGE_FILE_HEADER in lieu of a version number.
+ //
+ // If the CHILD_PROCESSES_ONLY flag is set, then the dll is blocked
+ // only when we are a child process.
+ enum Flags {
+ FLAGS_DEFAULT = 0,
+ BLOCK_WIN7_AND_OLDER = 1 << 0,
+ BLOCK_WIN8_AND_OLDER = 1 << 1,
+ USE_TIMESTAMP = 1 << 2,
+ CHILD_PROCESSES_ONLY = 1 << 3,
+ BROWSER_PROCESS_ONLY = 1 << 4,
+ REDIRECT_TO_NOOP_ENTRYPOINT = 1 << 5,
+ } mFlags;
+
+ bool IsVersionBlocked(const uint64_t aOther) const {
+ if (mMaxVersion == ALL_VERSIONS) {
+ return true;
+ }
+
+ return aOther <= mMaxVersion;
+ }
+
+ static const uint64_t ALL_VERSIONS = (uint64_t)-1LL;
+
+ // DLLs sometimes ship without a version number, particularly early
+ // releases. Blocking "version <= 0" has the effect of blocking unversioned
+ // DLLs (since the call to get version info fails), but not blocking
+ // any versioned instance.
+ static const uint64_t UNVERSIONED = 0ULL;
+};
+
+} // namespace mozilla
+
+// Convert the 4 (decimal) components of a DLL version number into a
+// single unsigned long long, as needed by the blocklist
+#if defined(_MSC_VER) && !defined(__clang__)
+
+// MSVC does not properly handle the constexpr MAKE_VERSION, so we use a macro
+// instead (ugh).
+# define MAKE_VERSION(a, b, c, d) \
+ ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL)
+
+#else
+
+static inline constexpr uint64_t MAKE_VERSION(uint16_t a, uint16_t b,
+ uint16_t c, uint16_t d) {
+ return static_cast<uint64_t>(a) << 48 | static_cast<uint64_t>(b) << 32 |
+ static_cast<uint64_t>(c) << 16 | static_cast<uint64_t>(d);
+}
+
+#endif
+
+#if !defined(DLL_BLOCKLIST_STRING_TYPE)
+# error "You must define DLL_BLOCKLIST_STRING_TYPE"
+#endif // !defined(DLL_BLOCKLIST_STRING_TYPE)
+
+#define DLL_BLOCKLIST_DEFINITIONS_BEGIN_NAMED(name) \
+ using DllBlockInfo = mozilla::DllBlockInfoT<DLL_BLOCKLIST_STRING_TYPE>; \
+ static const DllBlockInfo name[] = {
+#define DLL_BLOCKLIST_DEFINITIONS_BEGIN \
+ DLL_BLOCKLIST_DEFINITIONS_BEGIN_NAMED(gWindowsDllBlocklist)
+
+#define DLL_BLOCKLIST_DEFINITIONS_END \
+ {} \
+ } \
+ ;
+
+#define DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY_FOR(name, list) \
+ const DllBlockInfo* name = &list[0]
+
+#define DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(name) \
+ DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY_FOR(name, gWindowsDllBlocklist)
+
+#define DECLARE_POINTER_TO_LAST_DLL_BLOCKLIST_ENTRY_FOR(name, list) \
+ const DllBlockInfo* name = &list[mozilla::ArrayLength(list) - 1]
+
+#define DECLARE_POINTER_TO_LAST_DLL_BLOCKLIST_ENTRY(name) \
+ DECLARE_POINTER_TO_LAST_DLL_BLOCKLIST_ENTRY_FOR(name, gWindowsDllBlocklist)
+
+#define DECLARE_DLL_BLOCKLIST_NUM_ENTRIES_FOR(name, list) \
+ const size_t name = mozilla::ArrayLength(list) - 1
+
+#define DECLARE_DLL_BLOCKLIST_NUM_ENTRIES(name) \
+ DECLARE_DLL_BLOCKLIST_NUM_ENTRIES_FOR(name, gWindowsDllBlocklist)
+
+#endif // mozilla_WindowsDllBlocklistCommon_h
diff --git a/mozglue/dllservices/WindowsDllBlocklistDefs.in b/mozglue/dllservices/WindowsDllBlocklistDefs.in
new file mode 100644
index 0000000000..752a131f9f
--- /dev/null
+++ b/mozglue/dllservices/WindowsDllBlocklistDefs.in
@@ -0,0 +1,284 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# This file exposes three lists:
+# ALL_PROCESSES, BROWSER_PROCESS, and CHILD_PROCESSES
+#
+# In addition, each of those lists supports a special variant for test-only
+# entries:
+# ALL_PROCESSES_TESTS, BROWSER_PROCESS_TESTS, and CHILD_PROCESSES_TESTS
+#
+# Choose the list that is applicable to the applicable process type(s) for your
+# DLL block.
+#
+# The currently supported blocklist entry types are:
+# DllBlocklistEntry, A11yBlocklistEntry, LspBlocklistEntry,
+# RedirectToNoOpEntryPoint
+# (See gen_dll_blocklist_defs.py for their documentation.)
+#
+# Example:
+# ALL_PROCESSES += [
+# DllBlocklistEntry("foo.dll", (1, 2, 3, 4)),
+# DllBlocklistEntry("foo.dll", ALL_VERSIONS),
+# DllBlocklistEntry("foo.dll", UNVERSIONED),
+# DllBlocklistEntry("foo.dll", 0x0000123400000000),
+# DllBlocklistEntry("foo.dll", PETimeStamp(0x12345678)),
+# ]
+#
+# The version parameter the "last bad" version, that is, we block anything that
+# is less-than or equal to that version.
+
+ALL_PROCESSES += [
+ # NPFFAddon - Known malware
+ DllBlocklistEntry("npffaddon.dll", ALL_VERSIONS),
+
+ # AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted
+ DllBlocklistEntry("avgrsstx.dll", (8,5,0,401)),
+
+ # calc.dll - Suspected malware
+ DllBlocklistEntry("calc.dll", (1,0,0,1)),
+
+ # hook.dll - Suspected malware
+ DllBlocklistEntry("hook.dll", ALL_VERSIONS),
+
+ # GoogleDesktopNetwork3.dll - Extremely old, unversioned instances
+ # of this DLL cause crashes
+ DllBlocklistEntry("googledesktopnetwork3.dll", UNVERSIONED),
+
+ # rdolib.dll - Suspected malware
+ DllBlocklistEntry("rdolib.dll", (6,0,88,4)),
+
+ # fgjk4wvb.dll - Suspected malware
+ DllBlocklistEntry("fgjk4wvb.dll", (8,8,8,8)),
+
+ # radhslib.dll - Naomi internet filter - unmaintained since 2006
+ DllBlocklistEntry("radhslib.dll", UNVERSIONED),
+
+ # Music download filter for vkontakte.ru - old instances
+ # of this DLL cause crashes
+ DllBlocklistEntry("vksaver.dll", (2,2,2,0)),
+
+ # Topcrash in Firefox 4.0b1
+ DllBlocklistEntry("rlxf.dll", (1,2,323,1)),
+
+ # psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox
+ # Adobe photoshop library, now redundant in later installations
+ DllBlocklistEntry("psicon.dll", ALL_VERSIONS),
+
+ # Topcrash in Firefox 4 betas (bug 618899),
+ DllBlocklistEntry("accelerator.dll", (3,2,1,6)),
+
+ # Topcrash with Roboform in Firefox 8 (bug 699134),
+ DllBlocklistEntry("rf-firefox.dll", (7,6,1,0)),
+ DllBlocklistEntry("roboform.dll", (7,6,1,0)),
+
+ # Topcrash with Babylon Toolbar on FF16+ (bug 721264),
+ DllBlocklistEntry("babyfox.dll", ALL_VERSIONS),
+
+ # sprotector.dll crashes, bug 957258
+ DllBlocklistEntry("sprotector.dll", ALL_VERSIONS),
+
+ # Windows Media Foundation FLAC decoder and type sniffer (bug 839031).
+ DllBlocklistEntry("mfflac.dll", ALL_VERSIONS),
+
+ # Older Relevant Knowledge DLLs cause us to crash (bug 904001).
+ DllBlocklistEntry("rlnx.dll", (1, 3, 334, 9)),
+ DllBlocklistEntry("pmnx.dll", (1, 3, 334, 9)),
+ DllBlocklistEntry("opnx.dll", (1, 3, 334, 9)),
+ DllBlocklistEntry("prnx.dll", (1, 3, 334, 9)),
+
+ # Older belgian ID card software causes Firefox to crash or hang on
+ # shutdown, bug 831285 and 918399.
+ DllBlocklistEntry("beid35cardlayer.dll", (3, 5, 6, 6968)),
+
+ # bug 925459, bitguard crashes
+ DllBlocklistEntry("bitguard.dll", ALL_VERSIONS),
+
+ # bug 812683 - crashes in Windows library when Asus Gamer OSD is installed
+ # Software is discontinued/unsupported
+ DllBlocklistEntry("atkdx11disp.dll", ALL_VERSIONS),
+
+ # Topcrash with Conduit SearchProtect, bug 944542
+ DllBlocklistEntry("spvc32.dll", ALL_VERSIONS),
+
+ # Topcrash with V-bates, bug 1002748 and bug 1023239
+ DllBlocklistEntry("libinject.dll", UNVERSIONED),
+ DllBlocklistEntry("libinject2.dll", PETimeStamp(0x537DDC93)),
+ DllBlocklistEntry("libredir2.dll", PETimeStamp(0x5385B7ED)),
+
+ # Crashes with RoboForm2Go written against old SDK, bug 988311/1196859
+ DllBlocklistEntry("rf-firefox-22.dll", ALL_VERSIONS),
+ DllBlocklistEntry("rf-firefox-40.dll", ALL_VERSIONS),
+
+ # Crashes with DesktopTemperature, bug 1046382
+ DllBlocklistEntry("dtwxsvc.dll", PETimeStamp(0x53153234)),
+
+ # Startup crashes with Lenovo Onekey Theater, bug 1123778
+ DllBlocklistEntry("activedetect32.dll", UNVERSIONED),
+ DllBlocklistEntry("activedetect64.dll", UNVERSIONED),
+ DllBlocklistEntry("windowsapihookdll32.dll", UNVERSIONED),
+ DllBlocklistEntry("windowsapihookdll64.dll", UNVERSIONED),
+
+ # Flash crashes with RealNetworks RealDownloader, bug 1132663
+ DllBlocklistEntry("rndlnpshimswf.dll", ALL_VERSIONS),
+ DllBlocklistEntry("rndlmainbrowserrecordplugin.dll", ALL_VERSIONS),
+
+ # Startup crashes with RealNetworks Browser Record Plugin, bug 1170141
+ DllBlocklistEntry("nprpffbrowserrecordext.dll", ALL_VERSIONS),
+ DllBlocklistEntry("nprndlffbrowserrecordext.dll", ALL_VERSIONS),
+
+ # Crashes with CyberLink YouCam, bug 1136968
+ DllBlocklistEntry("ycwebcamerasource.ax", (2, 0, 0, 1611)),
+
+ # Old version of WebcamMax crashes WebRTC, bug 1130061
+ DllBlocklistEntry("vwcsource.ax", (1, 5, 0, 0)),
+
+ # NetOp School, discontinued product, bug 763395
+ DllBlocklistEntry("nlsp.dll", (6, 23, 2012, 19)),
+
+ # Orbit Downloader, bug 1222819
+ DllBlocklistEntry("grabdll.dll", (2, 6, 1, 0)),
+ DllBlocklistEntry("grabkernel.dll", (1, 0, 0, 1)),
+
+ # ESET, bug 1229252
+ DllBlocklistEntry("eoppmonitor.dll", ALL_VERSIONS),
+
+ # SS2OSD, bug 1262348
+ DllBlocklistEntry("ss2osd.dll", ALL_VERSIONS),
+ DllBlocklistEntry("ss2devprops.dll", ALL_VERSIONS),
+
+ # NHASUSSTRIXOSD.DLL, bug 1269244
+ DllBlocklistEntry("nhasusstrixosd.dll", ALL_VERSIONS),
+ DllBlocklistEntry("nhasusstrixdevprops.dll", ALL_VERSIONS),
+
+ # Crashes with PremierOpinion/RelevantKnowledge, bug 1277846
+ DllBlocklistEntry("opls.dll", ALL_VERSIONS),
+ DllBlocklistEntry("opls64.dll", ALL_VERSIONS),
+ DllBlocklistEntry("pmls.dll", ALL_VERSIONS),
+ DllBlocklistEntry("pmls64.dll", ALL_VERSIONS),
+ DllBlocklistEntry("prls.dll", ALL_VERSIONS),
+ DllBlocklistEntry("prls64.dll", ALL_VERSIONS),
+ DllBlocklistEntry("rlls.dll", ALL_VERSIONS),
+ DllBlocklistEntry("rlls64.dll", ALL_VERSIONS),
+
+ # Vorbis DirectShow filters, bug 1239690.
+ DllBlocklistEntry("vorbis.acm", (0, 0, 3, 6)),
+
+ # AhnLab Internet Security, bug 1311969
+ DllBlocklistEntry("nzbrcom.dll", ALL_VERSIONS),
+
+ # K7TotalSecurity, bug 1339083.
+ DllBlocklistEntry("k7pswsen.dll", (15, 2, 2, 95)),
+
+ # smci*.dll - goobzo crashware (bug 1339908),
+ DllBlocklistEntry("smci32.dll", ALL_VERSIONS),
+ DllBlocklistEntry("smci64.dll", ALL_VERSIONS),
+
+ # Crashes with Internet Download Manager, bug 1333486
+ DllBlocklistEntry("idmcchandler7.dll", ALL_VERSIONS),
+ DllBlocklistEntry("idmcchandler7_64.dll", ALL_VERSIONS),
+ DllBlocklistEntry("idmcchandler5.dll", ALL_VERSIONS),
+ DllBlocklistEntry("idmcchandler5_64.dll", ALL_VERSIONS),
+
+ # Nahimic 2 breaks applicaton update (bug 1356637),
+ DllBlocklistEntry("nahimic2devprops.dll", (2, 5, 19, 0xffff)),
+ # Nahimic is causing crashes, bug 1233556
+ DllBlocklistEntry("nahimicmsiosd.dll", UNVERSIONED),
+ # Nahimic is causing crashes, bug 1360029
+ DllBlocklistEntry("nahimicvrdevprops.dll", UNVERSIONED),
+ DllBlocklistEntry("nahimic2osd.dll", (2, 5, 19, 0xffff)),
+ DllBlocklistEntry("nahimicmsidevprops.dll", UNVERSIONED),
+
+ # Bug 1268470 - crashes with Kaspersky Lab on Windows 8
+ DllBlocklistEntry("klsihk64.dll", (14, 0, 456, 0xffff),
+ BLOCK_WIN8_AND_OLDER),
+
+ # Bug 1579758, crashes with OpenSC nightly version 0.19.0.448 and lower
+ DllBlocklistEntry("onepin-opensc-pkcs11.dll", (0, 19, 0, 448)),
+
+ # Avecto Privilege Guard causes crashes, bug 1385542
+ DllBlocklistEntry("pghook.dll", ALL_VERSIONS),
+
+ # Old versions of G DATA BankGuard, bug 1421991
+ DllBlocklistEntry("banksafe64.dll", (1, 2, 15299, 65535)),
+
+ # Old versions of G DATA, bug 1043775
+ DllBlocklistEntry("gdkbfltdll64.dll", (1, 0, 14141, 240)),
+
+ # Dell Backup and Recovery tool causes crashes, bug 1433408
+ DllBlocklistEntry("dbroverlayiconnotbackuped.dll", (1, 8, 0, 9)),
+ DllBlocklistEntry("dbroverlayiconbackuped.dll", (1, 8, 0, 9)),
+
+ # NVIDIA nView Desktop Management causes crashes, bug 1465787
+ DllBlocklistEntry("nviewh64.dll", (6, 14, 10, 14847)),
+
+ # Ivanti Endpoint Security, bug 1553776
+ DllBlocklistEntry("sxwmon.dll", ALL_VERSIONS),
+ DllBlocklistEntry("sxwmon64.dll", ALL_VERSIONS),
+
+ # 360 Safeguard/360 Total Security causes a11y crashes, bug 1536227.
+ DllBlocklistEntry("safemon64.dll", ALL_VERSIONS),
+
+ # Old versions of Digital Guardian, bug 1318858, bug 1603974,
+ # and bug 1672367
+ RedirectToNoOpEntryPoint("dgapi.dll", (7, 5, 0xffff, 0xffff)),
+ RedirectToNoOpEntryPoint("dgapi64.dll", (7, 5, 0xffff, 0xffff)),
+
+ # Old versions of COMODO Internet Security, bug 1608048
+ DllBlocklistEntry("IseGuard32.dll", (1, 6, 13835, 184)),
+ DllBlocklistEntry("IseGuard64.dll", (1, 6, 13835, 184)),
+
+ # Old version of COMODO Firewall, bug 1407712 and bug 1624336
+ DllBlocklistEntry("guard64.dll", (8, 4, 0, 65535)),
+
+ # Old version of Panda Security, bug 1637984
+ DllBlocklistEntry("PavLspHook64.dll", (9, 2, 2, 1), BLOCK_WIN7_AND_OLDER),
+]
+
+ALL_PROCESSES_TESTS += [
+ # DLLs used by TestDllBlocklist* gTests
+ DllBlocklistEntry("testdllblocklist_matchbyname.dll", ALL_VERSIONS),
+ DllBlocklistEntry("testdllblocklist_matchbyversion.dll", (5, 5, 5, 5)),
+ DllBlocklistEntry("testdllblocklist_allowbyversion.dll", (5, 5, 5, 5)),
+ RedirectToNoOpEntryPoint("testdllblocklist_noopentrypoint.dll",
+ (5, 5, 5, 5)),
+]
+
+BROWSER_PROCESS += [
+ # RealPlayer, bug 1418535, bug 1437417
+ # Versions before 18.1.11.0 cause severe performance problems.
+ A11yBlocklistEntry("dtvhooks.dll", (18, 1, 10, 0xffff)),
+ A11yBlocklistEntry("dtvhooks64.dll", (18, 1, 10, 0xffff)),
+
+ # SolidWorks Windows Explorer integration causes crashes, bug 1566109
+ # and bug 1468250
+ DllBlocklistEntry("Database.dll", ALL_VERSIONS),
+
+ # Hancom Office shell extension causes crashes when the file picker is
+ # opened. See bug 1581092.
+ DllBlocklistEntry("hncshellext64.dll", (1, 0, 0 ,3)),
+
+ # Cambridge Silicon Radio, bug 1634538
+ DllBlocklistEntry("BLEtokenCredentialProvider.dll", (2, 1, 63, 0)),
+
+ # FYunZip and PuddingZip, loaded as shell extension, cause crashes
+ # bug 1576728
+ DllBlocklistEntry("oly64.dll", (1, 1, 3, 19920)),
+ DllBlocklistEntry("oly.dll", (1, 1, 3, 19920)),
+ DllBlocklistEntry("pdzipmenu64.dll", (1, 4, 4, 20103)),
+ DllBlocklistEntry("pdzipmenu32.dll", (1, 4, 4, 20103)),
+
+ # McAfee Data Loss Prevention causes crashs with multiple signatures,
+ # bug 1634090
+ DllBlocklistEntry("fcagff.dll", (11, 6, 0xffff, 0xffff)),
+ DllBlocklistEntry("fcagff64.dll", (11, 6, 0xffff, 0xffff)),
+]
+
+CHILD_PROCESSES += [
+ # Causes crashes in the GPU process with WebRender enabled, bug 1544435
+ DllBlocklistEntry("wbload.dll", ALL_VERSIONS),
+]
+
diff --git a/mozglue/dllservices/WindowsDllServices.h b/mozglue/dllservices/WindowsDllServices.h
new file mode 100644
index 0000000000..e065c8ff6c
--- /dev/null
+++ b/mozglue/dllservices/WindowsDllServices.h
@@ -0,0 +1,211 @@
+/* -*- 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/. */
+
+#ifndef mozilla_glue_WindowsDllServices_h
+#define mozilla_glue_WindowsDllServices_h
+
+#include <utility>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Authenticode.h"
+#include "mozilla/LoaderAPIInterfaces.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "mozilla/WinHeaderOnlyUtils.h"
+#include "mozilla/WindowsDllBlocklist.h"
+#include "mozilla/mozalloc.h"
+
+#if defined(MOZILLA_INTERNAL_API)
+# include "MainThreadUtils.h"
+# include "nsISupportsImpl.h"
+# include "nsString.h"
+# include "nsThreadUtils.h"
+# include "prthread.h"
+# include "mozilla/SchedulerGroup.h"
+#endif // defined(MOZILLA_INTERNAL_API)
+
+// For PCUNICODE_STRING
+#include <winternl.h>
+
+namespace mozilla {
+namespace glue {
+namespace detail {
+
+class DllServicesBase : public Authenticode {
+ public:
+ /**
+ * WARNING: This method is called from within an unsafe context that holds
+ * multiple locks inside the Windows loader. The only thing that
+ * this function should be used for is dispatching the event to our
+ * event loop so that it may be handled in a safe context.
+ */
+ virtual void DispatchDllLoadNotification(ModuleLoadInfo&& aModLoadInfo) = 0;
+
+ /**
+ * This function accepts module load events to be processed later for
+ * the untrusted modules telemetry ping.
+ *
+ * WARNING: This method is run from within the Windows loader and should
+ * only perform trivial, loader-friendly operations.
+ */
+ virtual void DispatchModuleLoadBacklogNotification(
+ ModuleLoadInfoVec&& aEvents) = 0;
+
+ void SetAuthenticodeImpl(Authenticode* aAuthenticode) {
+ mAuthenticode = aAuthenticode;
+ }
+
+ void SetWinLauncherFunctions(const nt::WinLauncherFunctions& aFunctions) {
+ mWinLauncherFunctions = aFunctions;
+ }
+
+ template <typename... Args>
+ LauncherVoidResultWithLineInfo InitDllBlocklistOOP(Args&&... aArgs) {
+ MOZ_RELEASE_ASSERT(mWinLauncherFunctions.mInitDllBlocklistOOP);
+ return mWinLauncherFunctions.mInitDllBlocklistOOP(
+ std::forward<Args>(aArgs)...);
+ }
+
+ template <typename... Args>
+ void HandleLauncherError(Args&&... aArgs) {
+ MOZ_RELEASE_ASSERT(mWinLauncherFunctions.mHandleLauncherError);
+ mWinLauncherFunctions.mHandleLauncherError(std::forward<Args>(aArgs)...);
+ }
+
+ // In debug builds we override GetBinaryOrgName to add a Gecko-specific
+ // assertion. OTOH, we normally do not want people overriding this function,
+ // so we'll make it final in the release case, thus covering all bases.
+#if defined(DEBUG)
+ UniquePtr<wchar_t[]> GetBinaryOrgName(
+ const wchar_t* aFilePath,
+ AuthenticodeFlags aFlags = AuthenticodeFlags::Default) override
+#else
+ UniquePtr<wchar_t[]> GetBinaryOrgName(
+ const wchar_t* aFilePath,
+ AuthenticodeFlags aFlags = AuthenticodeFlags::Default) final
+#endif // defined(DEBUG)
+ {
+ if (!mAuthenticode) {
+ return nullptr;
+ }
+
+ return mAuthenticode->GetBinaryOrgName(aFilePath, aFlags);
+ }
+
+ virtual void DisableFull() { DllBlocklist_SetFullDllServices(nullptr); }
+
+ DllServicesBase(const DllServicesBase&) = delete;
+ DllServicesBase(DllServicesBase&&) = delete;
+ DllServicesBase& operator=(const DllServicesBase&) = delete;
+ DllServicesBase& operator=(DllServicesBase&&) = delete;
+
+ protected:
+ DllServicesBase() : mAuthenticode(nullptr) {}
+
+ virtual ~DllServicesBase() = default;
+
+ void EnableFull() { DllBlocklist_SetFullDllServices(this); }
+ void EnableBasic() { DllBlocklist_SetBasicDllServices(this); }
+
+ private:
+ Authenticode* mAuthenticode;
+ nt::WinLauncherFunctions mWinLauncherFunctions;
+};
+
+} // namespace detail
+
+#if defined(MOZILLA_INTERNAL_API)
+
+struct EnhancedModuleLoadInfo final {
+ explicit EnhancedModuleLoadInfo(ModuleLoadInfo&& aModLoadInfo)
+ : mNtLoadInfo(std::move(aModLoadInfo)) {
+ // Only populate mThreadName when we're on the same thread as the event
+ if (mNtLoadInfo.mThreadId == ::GetCurrentThreadId()) {
+ mThreadName = PR_GetThreadName(PR_GetCurrentThread());
+ }
+ MOZ_ASSERT(!mNtLoadInfo.mSectionName.IsEmpty());
+ }
+
+ EnhancedModuleLoadInfo(EnhancedModuleLoadInfo&&) = default;
+ EnhancedModuleLoadInfo& operator=(EnhancedModuleLoadInfo&&) = default;
+
+ EnhancedModuleLoadInfo(const EnhancedModuleLoadInfo&) = delete;
+ EnhancedModuleLoadInfo& operator=(const EnhancedModuleLoadInfo&) = delete;
+
+ nsDependentString GetSectionName() const {
+ return mNtLoadInfo.mSectionName.AsString();
+ }
+
+ using BacktraceType = decltype(ModuleLoadInfo::mBacktrace);
+
+ ModuleLoadInfo mNtLoadInfo;
+ nsCString mThreadName;
+};
+
+class DllServices : public detail::DllServicesBase {
+ public:
+ void DispatchDllLoadNotification(ModuleLoadInfo&& aModLoadInfo) final {
+ nsCOMPtr<nsIRunnable> runnable(
+ NewRunnableMethod<StoreCopyPassByRRef<EnhancedModuleLoadInfo>>(
+ "DllServices::NotifyDllLoad", this, &DllServices::NotifyDllLoad,
+ std::move(aModLoadInfo)));
+
+ SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget());
+ }
+
+ void DispatchModuleLoadBacklogNotification(
+ ModuleLoadInfoVec&& aEvents) final {
+ nsCOMPtr<nsIRunnable> runnable(
+ NewRunnableMethod<StoreCopyPassByRRef<ModuleLoadInfoVec>>(
+ "DllServices::NotifyModuleLoadBacklog", this,
+ &DllServices::NotifyModuleLoadBacklog, std::move(aEvents)));
+
+ SchedulerGroup::Dispatch(TaskCategory::Other, runnable.forget());
+ }
+
+# if defined(DEBUG)
+ UniquePtr<wchar_t[]> GetBinaryOrgName(
+ const wchar_t* aFilePath,
+ AuthenticodeFlags aFlags = AuthenticodeFlags::Default) final {
+ // This function may perform disk I/O, so we should never call it on the
+ // main thread.
+ MOZ_ASSERT(!NS_IsMainThread());
+ return detail::DllServicesBase::GetBinaryOrgName(aFilePath, aFlags);
+ }
+# endif // defined(DEBUG)
+
+ NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(DllServices)
+
+ protected:
+ DllServices() = default;
+ ~DllServices() = default;
+
+ virtual void NotifyDllLoad(EnhancedModuleLoadInfo&& aModLoadInfo) = 0;
+ virtual void NotifyModuleLoadBacklog(ModuleLoadInfoVec&& aEvents) = 0;
+};
+
+#else
+
+class BasicDllServices final : public detail::DllServicesBase {
+ public:
+ BasicDllServices() { EnableBasic(); }
+
+ ~BasicDllServices() = default;
+
+ // Not useful in this class, so provide a default implementation
+ virtual void DispatchDllLoadNotification(
+ ModuleLoadInfo&& aModLoadInfo) override {}
+
+ virtual void DispatchModuleLoadBacklogNotification(
+ ModuleLoadInfoVec&& aEvents) override {}
+};
+
+#endif // defined(MOZILLA_INTERNAL_API)
+
+} // namespace glue
+} // namespace mozilla
+
+#endif // mozilla_glue_WindowsDllServices_h
diff --git a/mozglue/dllservices/WindowsFallbackLoaderAPI.cpp b/mozglue/dllservices/WindowsFallbackLoaderAPI.cpp
new file mode 100644
index 0000000000..e80aa376a7
--- /dev/null
+++ b/mozglue/dllservices/WindowsFallbackLoaderAPI.cpp
@@ -0,0 +1,86 @@
+/* -*- 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/. */
+
+#include "WindowsFallbackLoaderAPI.h"
+
+namespace mozilla {
+
+ModuleLoadInfo FallbackLoaderAPI::ConstructAndNotifyBeginDllLoad(
+ void** aContext, PCUNICODE_STRING aRequestedDllName) {
+ ModuleLoadInfo loadInfo(aRequestedDllName);
+
+ MOZ_ASSERT(mLoaderObserver);
+ if (mLoaderObserver) {
+ mLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
+ }
+
+ return loadInfo;
+}
+
+bool FallbackLoaderAPI::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) {
+ MOZ_ASSERT(mLoaderObserver);
+ if (!mLoaderObserver) {
+ return false;
+ }
+
+ return mLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle);
+}
+
+void FallbackLoaderAPI::NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) {
+ aModuleLoadInfo.SetEndLoadTimeStamp();
+
+ if (NT_SUCCESS(aLoadNtStatus)) {
+ aModuleLoadInfo.CaptureBacktrace();
+ }
+
+ MOZ_ASSERT(mLoaderObserver);
+ if (mLoaderObserver) {
+ mLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus,
+ std::move(aModuleLoadInfo));
+ }
+}
+
+nt::AllocatedUnicodeString FallbackLoaderAPI::GetSectionName(
+ void* aSectionAddr) {
+ static const StaticDynamicallyLinkedFunctionPtr<decltype(
+ &::NtQueryVirtualMemory)>
+ pNtQueryVirtualMemory(L"ntdll.dll", "NtQueryVirtualMemory");
+ MOZ_ASSERT(pNtQueryVirtualMemory);
+
+ if (!pNtQueryVirtualMemory) {
+ return nt::AllocatedUnicodeString();
+ }
+
+ nt::MemorySectionNameBuf buf;
+ NTSTATUS ntStatus =
+ pNtQueryVirtualMemory(::GetCurrentProcess(), aSectionAddr,
+ MemorySectionName, &buf, sizeof(buf), nullptr);
+ if (!NT_SUCCESS(ntStatus)) {
+ return nt::AllocatedUnicodeString();
+ }
+
+ return nt::AllocatedUnicodeString(&buf.mSectionFileName);
+}
+
+nt::LoaderAPI::InitDllBlocklistOOPFnPtr
+FallbackLoaderAPI::GetDllBlocklistInitFn() {
+ MOZ_ASSERT_UNREACHABLE("This should not be called so soon!");
+ return nullptr;
+}
+
+nt::LoaderAPI::HandleLauncherErrorFnPtr
+FallbackLoaderAPI::GetHandleLauncherErrorFn() {
+ MOZ_ASSERT_UNREACHABLE("This should not be called so soon!");
+ return nullptr;
+}
+
+void FallbackLoaderAPI::SetObserver(nt::LoaderObserver* aLoaderObserver) {
+ mLoaderObserver = aLoaderObserver;
+}
+
+} // namespace mozilla
diff --git a/mozglue/dllservices/WindowsFallbackLoaderAPI.h b/mozglue/dllservices/WindowsFallbackLoaderAPI.h
new file mode 100644
index 0000000000..e0c4236b62
--- /dev/null
+++ b/mozglue/dllservices/WindowsFallbackLoaderAPI.h
@@ -0,0 +1,38 @@
+/* -*- 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_WindowsFallbackLoaderAPI_h
+#define mozilla_WindowsFallbackLoaderAPI_h
+
+#include "mozilla/Attributes.h"
+#include "NtLoaderAPI.h"
+
+namespace mozilla {
+
+class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS FallbackLoaderAPI final
+ : public nt::LoaderAPI {
+ public:
+ constexpr FallbackLoaderAPI() : mLoaderObserver(nullptr) {}
+
+ ModuleLoadInfo ConstructAndNotifyBeginDllLoad(
+ void** aContext, PCUNICODE_STRING aRequestedDllName) final;
+ bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
+ PHANDLE aOutHandle) final;
+ void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
+ ModuleLoadInfo&& aModuleLoadInfo) final;
+ nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final;
+ nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final;
+ nt::LoaderAPI::HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() final;
+
+ void SetObserver(nt::LoaderObserver* aLoaderObserver);
+
+ private:
+ nt::LoaderObserver* mLoaderObserver;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_WindowsFallbackLoaderAPI_h
diff --git a/mozglue/dllservices/gen_dll_blocklist_defs.py b/mozglue/dllservices/gen_dll_blocklist_defs.py
new file mode 100644
index 0000000000..cc71c6b49f
--- /dev/null
+++ b/mozglue/dllservices/gen_dll_blocklist_defs.py
@@ -0,0 +1,744 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+from __future__ import print_function
+
+from copy import deepcopy
+from six import iteritems, PY2
+from struct import unpack
+import os
+from uuid import UUID
+
+H_HEADER = """/* -*- 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 file was auto-generated from {0} by gen_dll_blocklist_data.py. */
+
+#ifndef mozilla_{1}_h
+#define mozilla_{1}_h
+
+"""
+
+H_FOOTER = """#endif // mozilla_{1}_h
+
+"""
+
+H_DEFS_BEGIN_DEFAULT = """#include "mozilla/WindowsDllBlocklistCommon.h"
+
+DLL_BLOCKLIST_DEFINITIONS_BEGIN
+
+"""
+
+H_DEFS_END_DEFAULT = """
+DLL_BLOCKLIST_DEFINITIONS_END
+
+"""
+
+H_BEGIN_LSP = """#include <guiddef.h>
+
+static const GUID gLayerGuids[] = {
+
+"""
+
+H_END_LSP = """
+};
+
+"""
+
+H_BEGIN_A11Y = """#include "mozilla/WindowsDllBlocklistCommon.h"
+
+DLL_BLOCKLIST_DEFINITIONS_BEGIN_NAMED(gBlockedInprocDlls)
+
+"""
+
+# These flag names should match the ones defined in WindowsDllBlocklistCommon.h
+FLAGS_DEFAULT = "FLAGS_DEFAULT"
+BLOCK_WIN8_AND_OLDER = "BLOCK_WIN8_AND_OLDER"
+BLOCK_WIN7_AND_OLDER = "BLOCK_WIN7_AND_OLDER"
+USE_TIMESTAMP = "USE_TIMESTAMP"
+CHILD_PROCESSES_ONLY = "CHILD_PROCESSES_ONLY"
+BROWSER_PROCESS_ONLY = "BROWSER_PROCESS_ONLY"
+SUBSTITUTE_LSP_PASSTHROUGH = "SUBSTITUTE_LSP_PASSTHROUGH"
+REDIRECT_TO_NOOP_ENTRYPOINT = "REDIRECT_TO_NOOP_ENTRYPOINT"
+
+# Only these flags are available in the input script
+INPUT_ONLY_FLAGS = {
+ BLOCK_WIN8_AND_OLDER,
+ BLOCK_WIN7_AND_OLDER,
+}
+
+
+def FILTER_ALLOW_ALL(entry):
+ # A11y entries are special, so we always exclude those by default
+ # (so it's not really allowing 'all', but it is simpler to reason about by
+ # pretending that it is allowing all.)
+ return not isinstance(entry, A11yBlocklistEntry)
+
+
+def FILTER_DENY_ALL(entry):
+ return False
+
+
+def FILTER_ALLOW_ONLY_A11Y(entry):
+ return isinstance(entry, A11yBlocklistEntry)
+
+
+def FILTER_ALLOW_ONLY_LSP(entry):
+ return isinstance(entry, LspBlocklistEntry)
+
+
+def FILTER_TESTS_ONLY(entry):
+ return not isinstance(entry, A11yBlocklistEntry) and entry.is_test()
+
+
+def derive_test_key(key):
+ return key + "_TESTS"
+
+
+ALL_DEFINITION_LISTS = ("ALL_PROCESSES", "BROWSER_PROCESS", "CHILD_PROCESSES")
+
+
+class BlocklistDescriptor(object):
+ """This class encapsulates every file that is output from this script.
+ Each instance has a name, an "input specification", and optional "flag
+ specification" and "output specification" entries.
+ """
+
+ DEFAULT_OUTSPEC = {
+ "mode": "",
+ "filter": FILTER_ALLOW_ALL,
+ "begin": H_DEFS_BEGIN_DEFAULT,
+ "end": H_DEFS_END_DEFAULT,
+ }
+
+ FILE_NAME_TPL = "WindowsDllBlocklist{0}Defs"
+
+ OutputDir = None
+ ExistingFd = None
+ ExistingFdLeafName = None
+
+ def __init__(self, name, inspec, **kwargs):
+ """Positional arguments:
+
+ name -- String containing the name of the output list.
+
+ inspec -- One or more members of ALL_DEFINITION_LISTS. The input used
+ for this blocklist file is the union of all lists specified by this
+ variable.
+
+ Keyword arguments:
+
+ outspec -- an optional list of dicts that specify how the lists output
+ will be written out to a file. Each dict may contain the following keys:
+
+ 'mode' -- a string that specifies a mode that is used when writing
+ out list entries to this particular output. This is passed in as the
+ mode argument to DllBlocklistEntry's write method.
+
+ 'filter' -- a function that, given a blocklist entry, decides
+ whether or not that entry shall be included in this output file.
+
+ 'begin' -- a string that is written to the output file after writing
+ the file's header, but prior to writing out any blocklist entries.
+
+ 'end' -- a string that is written to the output file after writing
+ out any blocklist entries but before the file's footer.
+
+ Any unspecified keys will be assigned default values.
+
+ flagspec -- an optional dict whose keys consist of one or more of the
+ list names from inspec. Each corresponding value is a set of flags that
+ should be applied to each entry from that list. For example, the
+ flagspec:
+
+ {'CHILD_PROCESSES': {CHILD_PROCESSES_ONLY}}
+
+ causes any blocklist entries from the CHILD_PROCESSES list to be output
+ with the CHILD_PROCESSES_ONLY flag set.
+
+ """
+
+ self._name = name
+
+ # inspec's elements must all come from ALL_DEFINITION_LISTS
+ assert not (set(inspec).difference(set(ALL_DEFINITION_LISTS)))
+
+ # Internally to the descriptor, we store input specifications as a dict
+ # that maps each input blocklist name to the set of flags to be applied
+ # to each entry in that blocklist.
+ self._inspec = {blocklist: set() for blocklist in inspec}
+
+ self._outspecs = kwargs.get("outspec", BlocklistDescriptor.DEFAULT_OUTSPEC)
+ if isinstance(self._outspecs, dict):
+ # _outspecs should always be stored as a list of dicts
+ self._outspecs = [self._outspecs]
+
+ flagspecs = kwargs.get("flagspec", dict())
+ # flagspec's keys must all come from ALL_DEFINITION_LISTS
+ assert not (set(flagspecs.keys()).difference(set(self._inspec.keys())))
+
+ # Merge the flags from flagspec into _inspec's sets
+ for blocklist, flagspec in iteritems(flagspecs):
+ spec = self._inspec[blocklist]
+ if not isinstance(spec, set):
+ raise TypeError("Flag spec for list %s must be a set!" % blocklist)
+ spec.update(flagspec)
+
+ @staticmethod
+ def set_output_fd(fd):
+ """The build system has already provided an open file descriptor for
+ one of our outputs. We save that here so that we may use that fd once
+ we're ready to process that fd's file. We also obtain the output dir for
+ use as the base directory for the other output files that we open.
+ """
+ BlocklistDescriptor.ExistingFd = fd
+ (
+ BlocklistDescriptor.OutputDir,
+ BlocklistDescriptor.ExistingFdLeafName,
+ ) = os.path.split(fd.name)
+
+ @staticmethod
+ def ensure_no_dupes(defs):
+ """Ensure that defs does not contain any dupes. We raise a ValueError
+ because this is a bug in the input and requires developer intervention.
+ """
+ seen = set()
+ for e in defs:
+ name = e.get_name()
+ if name not in seen:
+ seen.add(name)
+ else:
+ raise ValueError("Duplicate entry found: %s" % name)
+
+ @staticmethod
+ def get_test_entries(exec_env, blocklist):
+ """Obtains all test entries for the corresponding blocklist, and also
+ ensures that each entry has its test flag set.
+
+ Positional arguments:
+
+ exec_env -- dict containing the globals that were passed to exec to
+ when the input script was run.
+
+ blocklist -- The name of the blocklist to obtain tests from. This
+ should be one of the members of ALL_DEFINITION_LISTS
+ """
+ test_key = derive_test_key(blocklist)
+
+ def set_is_test(elem):
+ elem.set_is_test()
+ return elem
+
+ return map(set_is_test, exec_env[test_key])
+
+ def gen_list(self, exec_env, filter_func):
+ """Generates a sorted list of blocklist entries from the input script,
+ filtered via filter_func.
+
+ Positional arguments:
+
+ exec_env -- dict containing the globals that were passed to exec to
+ when the input script was run. This function expects exec_env to
+ contain lists of blocklist entries, keyed using one of the members of
+ ALL_DEFINITION_LISTS.
+
+ filter_func -- a filter function that evaluates each blocklist entry
+ to determine whether or not it should be included in the results.
+ """
+
+ # This list contains all the entries across all blocklists that we
+ # potentially want to process
+ unified_list = []
+
+ # For each blocklist specified in the _inspec, we query the globals
+ # for their entries, add any flags, and then add them to the
+ # unified_list.
+ for blocklist, listflags in iteritems(self._inspec):
+
+ def add_list_flags(elem):
+ # We deep copy so that flags set for an entry in one blocklist
+ # do not interfere with flags set for an entry in a different
+ # list.
+ result = deepcopy(elem)
+ result.add_flags(listflags)
+ return result
+
+ # We add list flags *before* filtering because the filters might
+ # want to access flags as part of their operation.
+ unified_list.extend(map(add_list_flags, exec_env[blocklist]))
+
+ # We also add any test entries for the lists specified by _inspec
+ unified_list.extend(
+ map(add_list_flags, self.get_test_entries(exec_env, blocklist))
+ )
+
+ # There should be no dupes in the input. If there are, raise an error.
+ self.ensure_no_dupes(unified_list)
+
+ # Now we filter out any unwanted list entries
+ filtered_list = filter(filter_func, unified_list)
+
+ # Sort the list on entry name so that the blocklist code may use
+ # binary search if it so chooses.
+ return sorted(filtered_list, key=lambda e: e.get_name())
+
+ @staticmethod
+ def get_fd(outspec_leaf_name):
+ """If BlocklistDescriptor.ExistingFd corresponds to outspec_leaf_name,
+ then we return that. Otherwise, we construct a new absolute path to
+ outspec_leaf_name and open a new file descriptor for writing.
+ """
+ if (
+ not BlocklistDescriptor.ExistingFd
+ or BlocklistDescriptor.ExistingFdLeafName != outspec_leaf_name
+ ):
+ new_name = os.path.join(BlocklistDescriptor.OutputDir, outspec_leaf_name)
+ return open(new_name, "w")
+
+ fd = BlocklistDescriptor.ExistingFd
+ BlocklistDescriptor.ExistingFd = None
+ return fd
+
+ def write(self, src_file, exec_env):
+ """Write out all output files that are specified by this descriptor.
+
+ Positional arguments:
+
+ src_file -- name of the input file from which the lists were generated.
+
+ exec_env -- dictionary containing the lists that were parsed from the
+ input file when it was executed.
+ """
+
+ for outspec in self._outspecs:
+ # Use DEFAULT_OUTSPEC to supply defaults for any unused outspec keys
+ effective_outspec = BlocklistDescriptor.DEFAULT_OUTSPEC.copy()
+ effective_outspec.update(outspec)
+
+ entries = self.gen_list(exec_env, effective_outspec["filter"])
+ if not entries:
+ continue
+
+ mode = effective_outspec["mode"]
+
+ # Since each output descriptor may generate output across multiple
+ # modes, each list is uniquified via the concatenation of our name
+ # and the mode.
+ listname = self._name + mode
+ leafname_no_ext = BlocklistDescriptor.FILE_NAME_TPL.format(listname)
+ leafname = leafname_no_ext + ".h"
+
+ with self.get_fd(leafname) as output:
+ print(H_HEADER.format(src_file, leafname_no_ext), file=output, end="")
+ print(effective_outspec["begin"], file=output, end="")
+
+ for e in entries:
+ e.write(output, mode)
+
+ print(effective_outspec["end"], file=output, end="")
+ print(H_FOOTER.format(src_file, leafname_no_ext), file=output, end="")
+
+
+A11Y_OUTPUT_SPEC = {
+ "filter": FILTER_ALLOW_ONLY_A11Y,
+ "begin": H_BEGIN_A11Y,
+}
+
+LSP_MODE_GUID = "Guid"
+
+LSP_OUTPUT_SPEC = [
+ {
+ "mode": LSP_MODE_GUID,
+ "filter": FILTER_ALLOW_ONLY_LSP,
+ "begin": H_BEGIN_LSP,
+ "end": H_END_LSP,
+ },
+]
+
+GENERATED_BLOCKLIST_FILES = [
+ BlocklistDescriptor("A11y", ["BROWSER_PROCESS"], outspec=A11Y_OUTPUT_SPEC),
+ BlocklistDescriptor(
+ "Launcher",
+ ALL_DEFINITION_LISTS,
+ flagspec={
+ "BROWSER_PROCESS": {BROWSER_PROCESS_ONLY},
+ "CHILD_PROCESSES": {CHILD_PROCESSES_ONLY},
+ },
+ ),
+ BlocklistDescriptor(
+ "Legacy",
+ ALL_DEFINITION_LISTS,
+ flagspec={
+ "BROWSER_PROCESS": {BROWSER_PROCESS_ONLY},
+ "CHILD_PROCESSES": {CHILD_PROCESSES_ONLY},
+ },
+ ),
+ # Roughed-in for the moment; we'll enable this in bug 1238735
+ # BlocklistDescriptor('LSP', ALL_DEFINITION_LISTS, outspec=LSP_OUTPUT_SPEC),
+ BlocklistDescriptor(
+ "Test", ALL_DEFINITION_LISTS, outspec={"filter": FILTER_TESTS_ONLY}
+ ),
+]
+
+
+class PETimeStamp(object):
+ def __init__(self, ts):
+ max_timestamp = (2 ** 32) - 1
+ if ts < 0 or ts > max_timestamp:
+ raise ValueError("Invalid timestamp value")
+ self._value = ts
+
+ def __str__(self):
+ return "0x%08XU" % self._value
+
+
+class Version(object):
+ """Encapsulates a DLL version."""
+
+ ALL_VERSIONS = 0xFFFFFFFFFFFFFFFF
+ UNVERSIONED = 0
+
+ def __init__(self, *args):
+ """There are multiple ways to construct a Version:
+
+ As a tuple containing four elements (recommended);
+ As four integral arguments;
+ As a PETimeStamp;
+ As a long integer.
+
+ The tuple and list formats require the value of each element to be
+ between 0 and 0xFFFF, inclusive.
+ """
+
+ self._ver = Version.UNVERSIONED
+
+ if len(args) == 1:
+ if isinstance(args[0], tuple):
+ self.validate_iterable(args[0])
+
+ self._ver = "MAKE_VERSION%r" % (args[0],)
+ elif isinstance(args[0], PETimeStamp):
+ self._ver = args[0]
+ else:
+ self._ver = int(args[0])
+ elif len(args) == 4:
+ self.validate_iterable(args)
+
+ self._ver = "MAKE_VERSION%r" % (tuple(args),)
+ else:
+ raise ValueError("Bad arguments to Version constructor")
+
+ def validate_iterable(self, arg):
+ if len(arg) != 4:
+ raise ValueError("Versions must be a 4-tuple")
+
+ for component in arg:
+ if not isinstance(component, int) or component < 0 or component > 0xFFFF:
+ raise ValueError(
+ "Each version component must be a 16-bit " "unsigned integer"
+ )
+
+ def build_long(self, args):
+ self.validate_iterable(args)
+ return (
+ (int(args[0]) << 48)
+ | (int(args[1]) << 32)
+ | (int(args[2]) << 16)
+ | int(args[3])
+ )
+
+ def is_timestamp(self):
+ return isinstance(self._ver, PETimeStamp)
+
+ def __str__(self):
+ if isinstance(self._ver, int):
+ if self._ver == Version.ALL_VERSIONS:
+ return "DllBlockInfo::ALL_VERSIONS"
+
+ if self._ver == Version.UNVERSIONED:
+ return "DllBlockInfo::UNVERSIONED"
+
+ return "0x%016XULL" % self._ver
+
+ return str(self._ver)
+
+
+class DllBlocklistEntry(object):
+ TEST_CONDITION = "defined(ENABLE_TESTS)"
+
+ def __init__(self, name, ver, flags=(), **kwargs):
+ """Positional arguments:
+
+ name -- The leaf name of the DLL.
+
+ ver -- The maximum version to be blocked. NB: The comparison used by the
+ blocklist is <=, so you should specify the last bad version, as opposed
+ to the first good version.
+
+ flags -- iterable containing the flags that should be applicable to
+ this blocklist entry.
+
+ Keyword arguments:
+
+ condition -- a string containing a C++ preprocessor expression. This
+ condition is written as a condition for an #if/#endif block that is
+ generated around the entry during output.
+ """
+
+ self.check_ascii(name)
+ self._name = name.lower()
+ self._ver = Version(ver)
+
+ self._flags = set()
+ self.add_flags(flags)
+ if self._ver.is_timestamp():
+ self._flags.add(USE_TIMESTAMP)
+
+ self._cond = kwargs.get("condition", set())
+ if isinstance(self._cond, str):
+ self._cond = {self._cond}
+
+ @staticmethod
+ def check_ascii(name):
+ if PY2:
+ if not all(ord(c) < 128 for c in name):
+ raise ValueError('DLL name "%s" must be ASCII!' % name)
+ return
+
+ try:
+ # Supported in Python 3.7
+ if not name.isascii():
+ raise ValueError('DLL name "%s" must be ASCII!' % name)
+ return
+ except AttributeError:
+ pass
+
+ try:
+ name.encode("ascii")
+ except UnicodeEncodeError:
+ raise ValueError('DLL name "%s" must be ASCII!' % name)
+
+ def get_name(self):
+ return self._name
+
+ def set_condition(self, cond):
+ self._cond.add(cond)
+
+ def get_condition(self):
+ if len(self._cond) == 1:
+ fmt = "{0}"
+ else:
+ fmt = "({0})"
+
+ return " && ".join([fmt.format(c) for c in self._cond])
+
+ def set_is_test(self):
+ self.set_condition(DllBlocklistEntry.TEST_CONDITION)
+
+ def is_test(self):
+ return self._cond and DllBlocklistEntry.TEST_CONDITION in self._cond
+
+ def add_flags(self, new_flags):
+ if isinstance(new_flags, str):
+ self._flags.add(new_flags)
+ else:
+ self._flags.update(new_flags)
+
+ @staticmethod
+ def get_flag_string(flag):
+ return "DllBlockInfo::" + flag
+
+ def get_flags_list(self):
+ return self._flags
+
+ def write(self, output, mode):
+ if self._cond:
+ print("#if %s" % self.get_condition(), file=output)
+
+ flags_str = ""
+
+ flags = self.get_flags_list()
+ if flags:
+ flags_str = ", " + " | ".join(map(self.get_flag_string, flags))
+
+ entry_str = ' DLL_BLOCKLIST_ENTRY("%s", %s%s)' % (
+ self._name,
+ str(self._ver),
+ flags_str,
+ )
+ print(entry_str, file=output)
+
+ if self._cond:
+ print("#endif // %s" % self.get_condition(), file=output)
+
+
+class A11yBlocklistEntry(DllBlocklistEntry):
+ """Represents a blocklist entry for injected a11y DLLs. This class does
+ not need to do anything special compared to a DllBlocklistEntry; we just
+ use this type to distinguish a11y entries from "regular" blocklist entries.
+ """
+
+ def __init__(self, name, ver, flags=(), **kwargs):
+ """These arguments are identical to DllBlocklistEntry.__init__"""
+
+ super(A11yBlocklistEntry, self).__init__(name, ver, flags, **kwargs)
+
+
+class RedirectToNoOpEntryPoint(DllBlocklistEntry):
+ """Represents a blocklist entry to hook the entrypoint into a function
+ just returning TRUE to keep a module alive and harmless.
+ This entry is intended to block a DLL which is injected by IAT patching
+ which is planted by a kernel callback routine for LoadImage because
+ blocking such a DLL makes a process fail to launch.
+ """
+
+ def __init__(self, name, ver, flags=(), **kwargs):
+ """These arguments are identical to DllBlocklistEntry.__init__"""
+
+ super(RedirectToNoOpEntryPoint, self).__init__(name, ver, flags, **kwargs)
+
+ def get_flags_list(self):
+ flags = super(RedirectToNoOpEntryPoint, self).get_flags_list()
+ # RedirectToNoOpEntryPoint items always include the following flag
+ flags.add(REDIRECT_TO_NOOP_ENTRYPOINT)
+ return flags
+
+
+class LspBlocklistEntry(DllBlocklistEntry):
+ """Represents a blocklist entry for a WinSock Layered Service Provider (LSP)."""
+
+ GUID_UNPACK_FMT_LE = "<IHHBBBBBBBB"
+ Guids = dict()
+
+ def __init__(self, name, ver, guids, flags=(), **kwargs):
+ """Positional arguments:
+
+ name -- The leaf name of the DLL.
+
+ ver -- The maximum version to be blocked. NB: The comparison used by the
+ blocklist is <=, so you should specify the last bad version, as opposed
+ to the first good version.
+
+ guids -- Either a string or list of strings containing the GUIDs that
+ uniquely identify the LSP. These GUIDs are generated by the developer of
+ the LSP and are registered with WinSock alongside the LSP. We record
+ this GUID as part of the "Winsock_LSP" annotation in our crash reports.
+
+ flags -- iterable containing the flags that should be applicable to
+ this blocklist entry.
+
+ Keyword arguments:
+
+ condition -- a string containing a C++ preprocessor expression. This
+ condition is written as a condition for an #if/#endif block that is
+ generated around the entry during output.
+ """
+
+ super(LspBlocklistEntry, self).__init__(name, ver, flags, **kwargs)
+ if not guids:
+ raise ValueError("Missing GUID(s)!")
+
+ if isinstance(guids, str):
+ self.insert(UUID(guids), name)
+ else:
+ for guid in guids:
+ self.insert(UUID(guid), name)
+
+ def insert(self, guid, name):
+ # Some explanation here: Multiple DLLs (and thus multiple instances of
+ # LspBlocklistEntry) may share the same GUIDs. To ensure that we do not
+ # have any duplicates, we store each GUID in a class variable, Guids.
+ # We include the original DLL name from the blocklist definitions so
+ # that we may output a comment above each GUID indicating which entries
+ # correspond to which GUID.
+ LspBlocklistEntry.Guids.setdefault(guid, []).append(name)
+
+ def get_flags_list(self):
+ flags = super(LspBlocklistEntry, self).get_flags_list()
+ # LSP entries always include the following flag
+ flags.add(SUBSTITUTE_LSP_PASSTHROUGH)
+ return flags
+
+ @staticmethod
+ def as_c_struct(guid, names):
+ parts = unpack(LspBlocklistEntry.GUID_UNPACK_FMT_LE, guid.bytes_le)
+ str_guid = (
+ " // %r\n // {%s}\n { 0x%08x, 0x%04x, 0x%04x, "
+ "{ 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x }"
+ " }"
+ % (
+ names,
+ str(guid),
+ parts[0],
+ parts[1],
+ parts[2],
+ parts[3],
+ parts[4],
+ parts[5],
+ parts[6],
+ parts[7],
+ parts[8],
+ parts[9],
+ parts[10],
+ )
+ )
+ return str_guid
+
+ def write(self, output, mode):
+ if mode != LSP_MODE_GUID:
+ super(LspBlocklistEntry, self).write(output, mode)
+ return
+
+ # We dump the entire contents of Guids on the first call, and then
+ # clear it. Remaining invocations of this method are no-ops.
+ if LspBlocklistEntry.Guids:
+ result = ",\n".join(
+ [
+ self.as_c_struct(guid, names)
+ for guid, names in iteritems(LspBlocklistEntry.Guids)
+ ]
+ )
+ print(result, file=output)
+ LspBlocklistEntry.Guids.clear()
+
+
+def exec_script_file(script_name, globals):
+ with open(script_name) as script:
+ exec(compile(script.read(), script_name, "exec"), globals)
+
+
+def gen_blocklists(first_fd, defs_filename):
+
+ BlocklistDescriptor.set_output_fd(first_fd)
+
+ # exec_env defines the global variables that will be present in the
+ # execution environment when defs_filename is run by exec.
+ exec_env = {
+ # Add the blocklist entry types
+ "A11yBlocklistEntry": A11yBlocklistEntry,
+ "DllBlocklistEntry": DllBlocklistEntry,
+ "LspBlocklistEntry": LspBlocklistEntry,
+ "RedirectToNoOpEntryPoint": RedirectToNoOpEntryPoint,
+ # Add the special version types
+ "ALL_VERSIONS": Version.ALL_VERSIONS,
+ "UNVERSIONED": Version.UNVERSIONED,
+ "PETimeStamp": PETimeStamp,
+ }
+
+ # Import definition lists into exec_env
+ for defname in ALL_DEFINITION_LISTS:
+ exec_env[defname] = []
+ # For each defname, add a special list for test-only entries
+ exec_env[derive_test_key(defname)] = []
+
+ # Import flags into exec_env
+ exec_env.update({flag: flag for flag in INPUT_ONLY_FLAGS})
+
+ # Now execute the input script with exec_env providing the globals
+ exec_script_file(defs_filename, exec_env)
+
+ # Tell the output descriptors to write out the output files.
+ for desc in GENERATED_BLOCKLIST_FILES:
+ desc.write(defs_filename, exec_env)
diff --git a/mozglue/dllservices/moz.build b/mozglue/dllservices/moz.build
new file mode 100644
index 0000000000..b46692fe84
--- /dev/null
+++ b/mozglue/dllservices/moz.build
@@ -0,0 +1,60 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"]:
+
+ SOURCES += [
+ # This file contains a |using namespace mozilla;| statement
+ "WindowsDllBlocklist.cpp",
+ ]
+
+ UNIFIED_SOURCES += [
+ "Authenticode.cpp",
+ "LoaderObserver.cpp",
+ "ModuleLoadFrame.cpp",
+ "WindowsFallbackLoaderAPI.cpp",
+ ]
+
+OS_LIBS += [
+ "crypt32",
+ "ntdll",
+ "version",
+ "wintrust",
+]
+
+DELAYLOAD_DLLS += [
+ "crypt32.dll",
+ "wintrust.dll",
+]
+
+EXPORTS.mozilla += [
+ "Authenticode.h",
+ "LoaderAPIInterfaces.h",
+ "ModuleLoadInfo.h",
+ "WindowsDllBlocklist.h",
+ "WindowsDllBlocklistCommon.h",
+]
+
+EXPORTS.mozilla.glue += [
+ "WindowsDllServices.h",
+]
+
+# Generate DLL Blocklists
+blocklist_header_types = ["A11y", "Launcher", "Legacy", "Test"]
+blocklist_file_leaf_tpl = "WindowsDllBlocklist{0}Defs.h"
+blocklist_files = tuple(
+ [blocklist_file_leaf_tpl.format(type) for type in blocklist_header_types]
+)
+
+GeneratedFile(
+ *blocklist_files,
+ script="gen_dll_blocklist_defs.py",
+ entry_point="gen_blocklists",
+ inputs=["WindowsDllBlocklistDefs.in"]
+)
+EXPORTS.mozilla += ["!" + hdr for hdr in blocklist_files]
+
+FINAL_LIBRARY = "mozglue"