summaryrefslogtreecommitdiffstats
path: root/toolkit/xre/dllservices/tests/gtest
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDLLBlocklist.cpp475
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/moz.build17
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/TestDllBlocklist_GMPluginProcessOnly.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/TestDllBlocklist_GPUProcessOnly.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/TestDllBlocklist_MatchByName.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/moz.build17
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/moz.build19
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/moz.build19
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/moz.build19
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.cpp12
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/moz.build21
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/moz.build17
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.rc42
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/moz.build17
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/TestDllBlocklist_SocketProcessOnly.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/TestDllBlocklist_UserBlocked.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/TestDllBlocklist_UtilityProcessOnly.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestUntrustedModules.cpp462
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc38
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/moz.build17
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp7
-rw-r--r--toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/moz.build15
-rw-r--r--toolkit/xre/dllservices/tests/gtest/moz.build39
-rw-r--r--toolkit/xre/dllservices/tests/gtest/rust/Cargo.toml12
-rw-r--r--toolkit/xre/dllservices/tests/gtest/rust/TestBCryptFallback.cpp113
-rw-r--r--toolkit/xre/dllservices/tests/gtest/rust/moz.build11
-rw-r--r--toolkit/xre/dllservices/tests/gtest/rust/test.rs19
48 files changed, 1890 insertions, 0 deletions
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDLLBlocklist.cpp b/toolkit/xre/dllservices/tests/gtest/TestDLLBlocklist.cpp
new file mode 100644
index 0000000000..938f63a524
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDLLBlocklist.cpp
@@ -0,0 +1,475 @@
+/* -*- 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 <windows.h>
+#include <winternl.h>
+
+#include <process.h>
+
+#include <array>
+#include <functional>
+
+#include "gtest/gtest.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Char16.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "mozilla/Services.h"
+#include "mozilla/WinDllServices.h"
+#include "mozilla/WindowsStackCookie.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIThread.h"
+#include "nsReadableUtils.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsUnicharUtils.h"
+#include "nsWindowsHelpers.h"
+
+static nsString GetFullPath(const nsAString& aLeaf) {
+ nsCOMPtr<nsIFile> f;
+
+ EXPECT_TRUE(NS_SUCCEEDED(
+ NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(f))));
+
+ EXPECT_NS_SUCCEEDED(f->Append(aLeaf));
+
+ bool exists;
+ EXPECT_TRUE(NS_SUCCEEDED(f->Exists(&exists)) && exists);
+
+ nsString ret;
+ EXPECT_NS_SUCCEEDED(f->GetPath(ret));
+ return ret;
+}
+
+void FlushMainThreadLoop() {
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ ASSERT_NS_SUCCEEDED(rv);
+
+ rv = NS_OK;
+ bool processed = true;
+ while (processed && NS_SUCCEEDED(rv)) {
+ rv = mainThread->ProcessNextEvent(false, &processed);
+ }
+}
+
+class TestDLLLoadObserver : public nsIObserver {
+ public:
+ using DLLFilter = std::function<bool(const char16_t*)>;
+
+ explicit TestDLLLoadObserver(DLLFilter dllFilter)
+ : mDllFilter(std::move(dllFilter)),
+ mMainThreadNotificationsCount(0),
+ mNonMainThreadNotificationsCount(0){};
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) override {
+ if (!mDllFilter(aData)) {
+ return NS_OK;
+ }
+ if (0 == strcmp(aTopic, mozilla::DllServices::kTopicDllLoadedMainThread)) {
+ ++mMainThreadNotificationsCount;
+ } else {
+ EXPECT_TRUE(
+ 0 ==
+ strcmp(aTopic, mozilla::DllServices::kTopicDllLoadedNonMainThread));
+ ++mNonMainThreadNotificationsCount;
+ }
+ return NS_OK;
+ }
+
+ void Init() {
+ nsCOMPtr<nsIObserverService> obsServ(
+ mozilla::services::GetObserverService());
+
+ EXPECT_TRUE(obsServ);
+
+ obsServ->AddObserver(this, mozilla::DllServices::kTopicDllLoadedMainThread,
+ false);
+ obsServ->AddObserver(
+ this, mozilla::DllServices::kTopicDllLoadedNonMainThread, false);
+ }
+
+ void Exit() {
+ // Observe only gets called if/when we flush the main thread loop.
+ FlushMainThreadLoop();
+
+ nsCOMPtr<nsIObserverService> obsServ(
+ mozilla::services::GetObserverService());
+
+ EXPECT_TRUE(obsServ);
+
+ obsServ->RemoveObserver(this,
+ mozilla::DllServices::kTopicDllLoadedMainThread);
+ obsServ->RemoveObserver(this,
+ mozilla::DllServices::kTopicDllLoadedNonMainThread);
+ }
+
+ int MainThreadNotificationsCount() { return mMainThreadNotificationsCount; }
+
+ int NonMainThreadNotificationsCount() {
+ return mNonMainThreadNotificationsCount;
+ }
+
+ private:
+ virtual ~TestDLLLoadObserver() = default;
+
+ DLLFilter mDllFilter;
+ int mMainThreadNotificationsCount;
+ int mNonMainThreadNotificationsCount;
+};
+
+NS_IMPL_ISUPPORTS(TestDLLLoadObserver, nsIObserver)
+
+TEST(TestDllBlocklist, BlockDllByName)
+{
+ // The DLL name has capital letters, so this also tests that the comparison
+ // is case-insensitive.
+ constexpr auto kLeafName = u"TestDllBlocklist_MatchByName.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafName.get()));
+
+ hDll.own(::LoadLibraryExW(dllPath.get(), nullptr, LOAD_LIBRARY_AS_DATAFILE));
+ // Mapped as MEM_MAPPED + PAGE_READONLY
+ EXPECT_TRUE(hDll);
+}
+
+TEST(TestDllBlocklist, BlockDllByVersion)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_MatchByVersion.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafName.get()));
+
+ hDll.own(
+ ::LoadLibraryExW(dllPath.get(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE));
+ // Mapped as MEM_IMAGE + PAGE_READONLY
+ EXPECT_TRUE(hDll);
+}
+
+TEST(TestDllBlocklist, AllowDllByVersion)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_AllowByVersion.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+}
+
+TEST(TestDllBlocklist, GPUProcessOnly_AllowInMainProcess)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_GPUProcessOnly.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+}
+
+TEST(TestDllBlocklist, SocketProcessOnly_AllowInMainProcess)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_SocketProcessOnly.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+}
+
+TEST(TestDllBlocklist, UtilityProcessOnly_AllowInMainProcess)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_UtilityProcessOnly.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+}
+
+TEST(TestDllBlocklist, GMPluginProcessOnly_AllowInMainProcess)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_GMPluginProcessOnly.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+}
+
+// This DLL has two entries; it's blocked for unversioned (i.e. DLLs that
+// have no version information) everywhere and blocked for versions 5.5.5.5 and
+// earlier only in the GPU process. Since the version we're trying to load
+// is 5.5.5.5, it should load in the main process.
+TEST(TestDllBlocklist, MultipleEntriesDifferentProcesses_AllowInMainProcess)
+{
+ constexpr auto kLeafName =
+ u"TestDllBlocklist_MultipleEntries_DifferentProcesses.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+}
+
+TEST(TestDllBlocklist, MultipleEntriesSameProcessBackward_Block)
+{
+ // One entry matches by version and many others do not, so
+ // we should block.
+ constexpr auto kLeafName =
+ u"TestDllBlocklist_MultipleEntries_SameProcessBackward.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafName.get()));
+
+ hDll.own(
+ ::LoadLibraryExW(dllPath.get(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE));
+ // Mapped as MEM_IMAGE + PAGE_READONLY
+ EXPECT_TRUE(hDll);
+}
+
+TEST(TestDllBlocklist, MultipleEntriesSameProcessForward_Block)
+{
+ // One entry matches by version and many others do not, so
+ // we should block.
+ constexpr auto kLeafName =
+ u"TestDllBlocklist_MultipleEntries_SameProcessForward.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafName.get()));
+
+ hDll.own(
+ ::LoadLibraryExW(dllPath.get(), nullptr, LOAD_LIBRARY_AS_IMAGE_RESOURCE));
+ // Mapped as MEM_IMAGE + PAGE_READONLY
+ EXPECT_TRUE(hDll);
+}
+
+#if defined(MOZ_LAUNCHER_PROCESS)
+// RedirectToNoOpEntryPoint needs the launcher process.
+// This test will fail in debug x64 if we mistakenly reintroduce stack buffers
+// in patched_NtMapViewOfSection (see bug 1733532).
+TEST(TestDllBlocklist, NoOpEntryPoint)
+{
+ // DllMain of this dll has MOZ_RELEASE_ASSERT. This test makes sure we load
+ // the module successfully without running DllMain.
+ constexpr auto kLeafName = u"TestDllBlocklist_NoOpEntryPoint.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+# if defined(MOZ_ASAN)
+ // With ASAN, the test uses mozglue's blocklist where
+ // REDIRECT_TO_NOOP_ENTRYPOINT is ignored. So LoadLibraryW
+ // is expected to fail.
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafName.get()));
+# else
+ EXPECT_TRUE(!!hDll);
+ EXPECT_TRUE(!!::GetModuleHandleW(kLeafName.get()));
+# endif
+}
+
+// User blocklist needs the launcher process.
+// This test will fail in debug x64 if we mistakenly reintroduce stack buffers
+// in patched_NtMapViewOfSection (see bug 1733532).
+TEST(TestDllBlocklist, UserBlocked)
+{
+ constexpr auto kLeafName = u"TestDllBlocklist_UserBlocked.dll"_ns;
+ nsString dllPath = GetFullPath(kLeafName);
+
+ nsModuleHandle hDll(::LoadLibraryW(dllPath.get()));
+
+// With ASAN, the test uses mozglue's blocklist where
+// the user blocklist is not used.
+# if !defined(MOZ_ASAN)
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafName.get()));
+# endif
+ hDll.own(::LoadLibraryExW(dllPath.get(), nullptr, LOAD_LIBRARY_AS_DATAFILE));
+ // Mapped as MEM_MAPPED + PAGE_READONLY
+ EXPECT_TRUE(hDll);
+}
+#endif // defined(MOZ_LAUNCHER_PROCESS)
+
+#define DLL_BLOCKLIST_ENTRY(name, ...) {name, __VA_ARGS__},
+#define DLL_BLOCKLIST_STRING_TYPE const char*
+#include "mozilla/WindowsDllBlocklistLegacyDefs.h"
+
+TEST(TestDllBlocklist, BlocklistIntegrity)
+{
+ DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(pFirst);
+ DECLARE_POINTER_TO_LAST_DLL_BLOCKLIST_ENTRY(pLast);
+
+ EXPECT_FALSE(pLast->mName || pLast->mMaxVersion || pLast->mFlags);
+
+ for (size_t i = 0; i < mozilla::ArrayLength(gWindowsDllBlocklist) - 1; ++i) {
+ auto pEntry = pFirst + i;
+
+ // Validate name
+ EXPECT_TRUE(!!pEntry->mName);
+ EXPECT_GT(strlen(pEntry->mName), 3U);
+
+ // Check the filename for valid characters.
+ for (auto pch = pEntry->mName; *pch != 0; ++pch) {
+ EXPECT_FALSE(*pch >= 'A' && *pch <= 'Z');
+ }
+ }
+}
+
+TEST(TestDllBlocklist, BlockThreadWithLoadLibraryEntryPoint)
+{
+ // Only supported on Nightly
+#if defined(NIGHTLY_BUILD)
+ using ThreadProc = unsigned(__stdcall*)(void*);
+
+ constexpr auto kLeafNameW = u"TestDllBlocklist_MatchByVersion.dll"_ns;
+
+ nsString fullPathW = GetFullPath(kLeafNameW);
+ EXPECT_FALSE(fullPathW.IsEmpty());
+
+ nsAutoHandle threadW(reinterpret_cast<HANDLE>(
+ _beginthreadex(nullptr, 0, reinterpret_cast<ThreadProc>(&::LoadLibraryW),
+ (void*)fullPathW.get(), 0, nullptr)));
+
+ EXPECT_TRUE(!!threadW);
+ EXPECT_EQ(::WaitForSingleObject(threadW, INFINITE), WAIT_OBJECT_0);
+
+# if !defined(MOZ_ASAN)
+ // ASAN builds under Windows 11 can have unexpected thread exit codes.
+ // See bug 1798796
+ DWORD exitCode;
+ EXPECT_TRUE(::GetExitCodeThread(threadW, &exitCode) && !exitCode);
+# endif // !defined(MOZ_ASAN)
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafNameW.get()));
+
+ const NS_LossyConvertUTF16toASCII fullPathA(fullPathW);
+ EXPECT_FALSE(fullPathA.IsEmpty());
+
+ nsAutoHandle threadA(reinterpret_cast<HANDLE>(
+ _beginthreadex(nullptr, 0, reinterpret_cast<ThreadProc>(&::LoadLibraryA),
+ (void*)fullPathA.get(), 0, nullptr)));
+
+ EXPECT_TRUE(!!threadA);
+ EXPECT_EQ(::WaitForSingleObject(threadA, INFINITE), WAIT_OBJECT_0);
+# if !defined(MOZ_ASAN)
+ // ASAN builds under Windows 11 can have unexpected thread exit codes.
+ // See bug 1798796
+ EXPECT_TRUE(::GetExitCodeThread(threadA, &exitCode) && !exitCode);
+# endif // !defined(MOZ_ASAN)
+ EXPECT_TRUE(!::GetModuleHandleW(kLeafNameW.get()));
+#endif // defined(NIGHTLY_BUILD)
+}
+
+constexpr auto kSingleNotificationDll1Loads = 4;
+constexpr auto kSingleNotificationDll2Loads = 3;
+using DllFullPathsArray =
+ std::array<nsString,
+ kSingleNotificationDll1Loads + kSingleNotificationDll2Loads>;
+
+DWORD __stdcall LoadSingleNotificationModules(LPVOID aThreadParameter) {
+ auto* dllFullPaths = reinterpret_cast<DllFullPathsArray*>(aThreadParameter);
+
+ for (const auto& dllFullPath : *dllFullPaths) {
+ nsModuleHandle hDll(::LoadLibraryW(dllFullPath.get()));
+ EXPECT_TRUE(!hDll);
+ EXPECT_TRUE(!::GetModuleHandleW(dllFullPath.get()));
+ }
+
+ return 0;
+}
+
+// The next test is only relevant if we hook LdrLoadDll, so we reflect the
+// hooking condition from browser/app/winlauncher/DllBlocklistInit.cpp.
+#if !defined(MOZ_ASAN) && !defined(_M_ARM64)
+
+// This test relies on the fact that blocked DLL loads generate a DLL load
+// notification.
+TEST(TestDllBlocklist, SingleNotification)
+{
+ // We will block-load the two DLLs multiple times, with variations on case.
+ std::array<nsLiteralString, kSingleNotificationDll1Loads> dll1Variations{
+ u"TestDllBlocklist_SingleNotification1.dll"_ns,
+ u"TestDllBlocklist_SingleNotification1.dll"_ns,
+ u"testdllblocklist_singlenotification1.dll"_ns,
+ u"TeStDlLbLoCkLiSt_SiNgLeNoTiFiCaTiOn1.DlL"_ns,
+ };
+ std::array<nsLiteralString, kSingleNotificationDll2Loads> dll2Variations{
+ u"TestDllBlocklist_SingleNotification2.dll"_ns,
+ u"testdllblocklist_singlenotification2.dll"_ns,
+ u"TESTDLLBLOCKLIST_SINGLENOTIFICATION2.dll"_ns,
+ };
+ DllFullPathsArray dllFullPaths;
+ size_t i = 0;
+ for (const auto& dllName : dll1Variations) {
+ dllFullPaths[i] = GetFullPath(dllName);
+ ++i;
+ }
+ for (const auto& dllName : dll2Variations) {
+ dllFullPaths[i] = GetFullPath(dllName);
+ ++i;
+ }
+
+ // Register our observer.
+ TestDLLLoadObserver::DLLFilter dllFilter(
+ [](const char16_t* aLoadedPath) -> bool {
+ nsDependentString loadedPath(aLoadedPath);
+ return StringEndsWith(loadedPath,
+ u"\\testdllblocklist_singlenotification1.dll"_ns,
+ nsCaseInsensitiveStringComparator) ||
+ StringEndsWith(loadedPath,
+ u"\\testdllblocklist_singlenotification2.dll"_ns,
+ nsCaseInsensitiveStringComparator);
+ });
+ RefPtr<TestDLLLoadObserver> obs(new TestDLLLoadObserver(dllFilter));
+ obs->Init();
+
+ // Load DllServices. This is required for notifications to get dispatched.
+ RefPtr<mozilla::DllServices> dllSvc(mozilla::DllServices::Get());
+ EXPECT_TRUE(dllSvc);
+
+ // Block-load the two DLLs multiple times on the main thread.
+ LoadSingleNotificationModules(reinterpret_cast<void*>(&dllFullPaths));
+
+ // Block-load the two DLLs multiple times on a different thread.
+ HANDLE thread =
+ ::CreateThread(nullptr, 0, LoadSingleNotificationModules,
+ reinterpret_cast<void*>(&dllFullPaths), 0, nullptr);
+ EXPECT_EQ(::WaitForSingleObject(thread, 5000), WAIT_OBJECT_0);
+
+ // Unregister our observer and flush the main thread loop.
+ obs->Exit();
+
+ // Check how many notifications we received
+ EXPECT_EQ(obs->MainThreadNotificationsCount(), 2);
+ EXPECT_EQ(obs->NonMainThreadNotificationsCount(),
+ kSingleNotificationDll1Loads + kSingleNotificationDll2Loads);
+}
+
+#endif // !defined(MOZ_ASAN) && !defined(_M_ARM64)
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.rc
new file mode 100644
index 0000000000..f56aa099ff
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/TestDllBlocklist_AllowByVersion.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,6
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_AllowByVersion.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/moz.build
new file mode 100644
index 0000000000..0987cdde1a
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_AllowByVersion/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_AllowByVersion")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_AllowByVersion.cpp",
+]
+
+RCFILE = "TestDllBlocklist_AllowByVersion.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_AllowByVersion.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/TestDllBlocklist_GMPluginProcessOnly.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/TestDllBlocklist_GMPluginProcessOnly.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/TestDllBlocklist_GMPluginProcessOnly.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/moz.build
new file mode 100644
index 0000000000..a5c89c2b8e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GMPluginProcessOnly/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_GMPluginProcessOnly")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_GMPluginProcessOnly.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_GMPluginProcessOnly.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/TestDllBlocklist_GPUProcessOnly.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/TestDllBlocklist_GPUProcessOnly.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/TestDllBlocklist_GPUProcessOnly.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/moz.build
new file mode 100644
index 0000000000..748e9cf22c
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_GPUProcessOnly/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_GPUProcessOnly")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_GPUProcessOnly.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_GPUProcessOnly.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/TestDllBlocklist_MatchByName.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/TestDllBlocklist_MatchByName.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/TestDllBlocklist_MatchByName.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/moz.build
new file mode 100644
index 0000000000..f34931898a
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByName/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_MatchByName")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_MatchByName.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_MatchByName.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.rc
new file mode 100644
index 0000000000..7390c1cb34
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/TestDllBlocklist_MatchByVersion.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_MatchByVersion.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/moz.build
new file mode 100644
index 0000000000..38e10524c7
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MatchByVersion/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_MatchByVersion")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_MatchByVersion.cpp",
+]
+
+RCFILE = "TestDllBlocklist_MatchByVersion.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_MatchByVersion.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.rc
new file mode 100644
index 0000000000..2b5b81406e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/TestDllBlocklist_MultipleEntries_DifferentProcesses.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_MultipleEntries_DifferentProcesses.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/moz.build
new file mode 100644
index 0000000000..cf2f745a28
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_DifferentProcesses/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_MultipleEntries_DifferentProcesses")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_MultipleEntries_DifferentProcesses.cpp",
+]
+
+RCFILE = "TestDllBlocklist_MultipleEntries_DifferentProcesses.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += [
+ "!TestDllBlocklist_MultipleEntries_DifferentProcesses.dll"
+ ]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.rc
new file mode 100644
index 0000000000..0b2431c343
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/TestDllBlocklist_MultipleEntries_SameProcessBackward.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_MultipleEntries_SameProcessBackward.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/moz.build
new file mode 100644
index 0000000000..21ab229245
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessBackward/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_MultipleEntries_SameProcessBackward")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_MultipleEntries_SameProcessBackward.cpp",
+]
+
+RCFILE = "TestDllBlocklist_MultipleEntries_SameProcessBackward.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += [
+ "!TestDllBlocklist_MultipleEntries_SameProcessBackward.dll"
+ ]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.rc
new file mode 100644
index 0000000000..20d14e24d7
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/TestDllBlocklist_MultipleEntries_SameProcessForward.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_MultipleEntries_SameProcessForward.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/moz.build
new file mode 100644
index 0000000000..1546489efb
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_MultipleEntries_SameProcessForward/moz.build
@@ -0,0 +1,19 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_MultipleEntries_SameProcessForward")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_MultipleEntries_SameProcessForward.cpp",
+]
+
+RCFILE = "TestDllBlocklist_MultipleEntries_SameProcessForward.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += [
+ "!TestDllBlocklist_MultipleEntries_SameProcessForward.dll"
+ ]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.cpp
new file mode 100644
index 0000000000..2505b8b700
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.cpp
@@ -0,0 +1,12 @@
+/* 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 "mozilla/Assertions.h"
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) {
+ MOZ_RELEASE_ASSERT(0);
+ return TRUE;
+}
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.rc
new file mode 100644
index 0000000000..7c79dac373
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/TestDllBlocklist_NoOpEntryPoint.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_NoOpEntryPoint.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/moz.build
new file mode 100644
index 0000000000..e9a10a150a
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_NoOpEntryPoint/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_NoOpEntryPoint")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_NoOpEntryPoint.cpp",
+]
+
+RCFILE = "TestDllBlocklist_NoOpEntryPoint.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_NoOpEntryPoint.dll"]
+
+OS_LIBS += [
+ "uuid",
+]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.cpp
new file mode 100644
index 0000000000..4f6ce877eb
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.rc
new file mode 100644
index 0000000000..90f098b1b9
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/TestDllBlocklist_SingleNotification1.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_SingleNotification1.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/moz.build
new file mode 100644
index 0000000000..79139b2b6e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification1/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_SingleNotification1")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_SingleNotification1.cpp",
+]
+
+RCFILE = "TestDllBlocklist_SingleNotification1.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_SingleNotification1.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.cpp
new file mode 100644
index 0000000000..4f6ce877eb
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.rc b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.rc
new file mode 100644
index 0000000000..0cd44b2f9b
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/TestDllBlocklist_SingleNotification2.rc
@@ -0,0 +1,42 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 5,5,5,5
+ PRODUCTVERSION 5,5,5,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "mozilla.org"
+ VALUE "FileDescription", L"Test DLL"
+ VALUE "FileVersion", "1.0"
+ VALUE "InternalName", "Test DLL"
+ VALUE "OriginalFilename", "TestDllBlocklist_SingleNotification2.dll"
+ VALUE "ProductName", "Test DLL"
+ VALUE "ProductVersion", "1.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/moz.build
new file mode 100644
index 0000000000..64d551f480
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SingleNotification2/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_SingleNotification2")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_SingleNotification2.cpp",
+]
+
+RCFILE = "TestDllBlocklist_SingleNotification2.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_SingleNotification2.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/TestDllBlocklist_SocketProcessOnly.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/TestDllBlocklist_SocketProcessOnly.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/TestDllBlocklist_SocketProcessOnly.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/moz.build
new file mode 100644
index 0000000000..dc93544e1b
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_SocketProcessOnly/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_SocketProcessOnly")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_SocketProcessOnly.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_SocketProcessOnly.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/TestDllBlocklist_UserBlocked.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/TestDllBlocklist_UserBlocked.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/TestDllBlocklist_UserBlocked.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/moz.build
new file mode 100644
index 0000000000..31996c5cb2
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UserBlocked/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_UserBlocked")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_UserBlocked.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_UserBlocked.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/TestDllBlocklist_UtilityProcessOnly.cpp b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/TestDllBlocklist_UtilityProcessOnly.cpp
new file mode 100644
index 0000000000..7bd936296e
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/TestDllBlocklist_UtilityProcessOnly.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/moz.build b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/moz.build
new file mode 100644
index 0000000000..913d0f155c
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestDllBlocklist_UtilityProcessOnly/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestDllBlocklist_UtilityProcessOnly")
+
+UNIFIED_SOURCES = [
+ "TestDllBlocklist_UtilityProcessOnly.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestDllBlocklist_UtilityProcessOnly.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules.cpp b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules.cpp
new file mode 100644
index 0000000000..ba4d1a8df8
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules.cpp
@@ -0,0 +1,462 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+
+#include "js/RegExp.h"
+#include "mozilla/BinarySearch.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/UntrustedModulesProcessor.h"
+#include "mozilla/WinDllServices.h"
+#include "nsContentUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "TelemetryFixture.h"
+#include "UntrustedModulesBackupService.h"
+#include "UntrustedModulesDataSerializer.h"
+
+using namespace mozilla;
+
+class ModuleLoadCounter final {
+ nsTHashMap<nsStringCaseInsensitiveHashKey, int> mCounters;
+
+ public:
+ template <size_t N>
+ ModuleLoadCounter(const nsString (&aNames)[N], const int (&aCounts)[N])
+ : mCounters(N) {
+ for (size_t i = 0; i < N; ++i) {
+ mCounters.InsertOrUpdate(aNames[i], aCounts[i]);
+ }
+ }
+
+ template <size_t N>
+ bool Remains(const nsString (&aNames)[N], const int (&aCounts)[N]) {
+ EXPECT_EQ(mCounters.Count(), N);
+ if (mCounters.Count() != N) {
+ return false;
+ }
+
+ bool result = true;
+ for (size_t i = 0; i < N; ++i) {
+ auto entry = mCounters.Lookup(aNames[i]);
+ if (!entry) {
+ wprintf(L"%s is not registered.\n",
+ static_cast<const wchar_t*>(aNames[i].get()));
+ result = false;
+ } else if (*entry != aCounts[i]) {
+ // We can return false, but let's print out all unmet modules
+ // which may be helpful to investigate test failures.
+ wprintf(L"%s:%4d\n", static_cast<const wchar_t*>(aNames[i].get()),
+ *entry);
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ bool IsDone() const {
+ bool allZero = true;
+ for (const auto& data : mCounters.Values()) {
+ if (data < 0) {
+ // If any counter is negative, we know the test fails.
+ // No need to continue.
+ return true;
+ }
+ if (data > 0) {
+ allZero = false;
+ }
+ }
+ // If all counters are zero, the test finished nicely. Otherwise, those
+ // counters are expected to be decremented later. Let's continue.
+ return allZero;
+ }
+
+ void Decrement(const nsString& aName) {
+ if (auto entry = mCounters.Lookup(aName)) {
+ --(*entry);
+ }
+ }
+};
+
+class UntrustedModulesCollector {
+ static constexpr int kMaximumAttempts = 500;
+ Vector<UntrustedModulesData> mData;
+ ModuleLoadCounter* mChecker = nullptr;
+ Maybe<nsresult> mRv;
+ int mAttempts = 0;
+
+ void PollUntrustedModulesData() {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetUntrustedModulesData()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [this](Maybe<UntrustedModulesData>&& aResult) {
+ // Some of expected loaded modules are still missing after
+ // kMaximumAttempts queries were submitted.
+ // Giving up here to avoid an infinite loop.
+ if (++mAttempts > kMaximumAttempts) {
+ mRv = Some(NS_ERROR_ABORT);
+ return;
+ }
+
+ if (aResult.isSome()) {
+ wprintf(L"Received data. (attempts=%d)\n", mAttempts);
+ for (auto item : aResult.ref().mEvents) {
+ mChecker->Decrement(item->mEvent.mRequestedDllName);
+ }
+ EXPECT_TRUE(mData.emplaceBack(std::move(aResult.ref())));
+ }
+
+ if (mChecker->IsDone()) {
+ mRv = Some(NS_OK);
+ return;
+ }
+
+ PollUntrustedModulesData();
+ },
+ [this](nsresult aReason) {
+ wprintf(L"GetUntrustedModulesData() failed - %08x\n", aReason);
+ EXPECT_TRUE(false);
+ mRv = Some(aReason);
+ });
+ }
+
+ public:
+ Vector<UntrustedModulesData>& Data() { return mData; }
+
+ nsresult Collect(ModuleLoadCounter& aChecker) {
+ mRv = Nothing();
+ mChecker = &aChecker;
+ mAttempts = 0;
+ mData.clear();
+
+ PollUntrustedModulesData();
+
+ EXPECT_TRUE(SpinEventLoopUntil("xre:UntrustedModulesCollector"_ns,
+ [this]() { return mRv.isSome(); }));
+
+ mChecker = nullptr;
+ return *mRv;
+ }
+};
+
+class UntrustedModulesFixture : public TelemetryTestFixture {
+ static constexpr int kLoadCountBeforeDllServices = 5;
+ static constexpr int kLoadCountAfterDllServices = 5;
+ static constexpr uint32_t kMaxModulesArrayLen = 10;
+
+ // One of the important test scenarios is to load modules before DllServices
+ // is initialized and to make sure those loading events are forwarded when
+ // DllServices is initialized.
+ // However, GTest instantiates a Fixture class every testcase and there is
+ // no way to re-enable DllServices and UntrustedModulesProcessor once it's
+ // disabled, which means no matter how many testcases we have, only the
+ // first testcase exercises that scenario. That's why we implement that
+ // test scenario in InitialModuleLoadOnce as a static member and runs it
+ // in the first testcase to be executed.
+ static INIT_ONCE sInitLoadOnce;
+ static UntrustedModulesCollector sInitLoadDataCollector;
+
+ static nsString PrependWorkingDir(const nsAString& aLeaf) {
+ nsCOMPtr<nsIFile> file;
+ EXPECT_TRUE(NS_SUCCEEDED(NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR,
+ getter_AddRefs(file))));
+ EXPECT_NS_SUCCEEDED(file->Append(aLeaf));
+ bool exists;
+ EXPECT_TRUE(NS_SUCCEEDED(file->Exists(&exists)) && exists);
+ nsString fullPath;
+ EXPECT_NS_SUCCEEDED(file->GetPath(fullPath));
+ return fullPath;
+ }
+
+ static BOOL CALLBACK InitialModuleLoadOnce(PINIT_ONCE, void*, void**);
+
+ protected:
+ static constexpr int kInitLoadCount =
+ kLoadCountBeforeDllServices + kLoadCountAfterDllServices;
+ static const nsString kTestModules[];
+
+ static void ValidateUntrustedModules(const UntrustedModulesData& aData,
+ bool aIsTruncatedData = false);
+
+ static void LoadAndFree(const nsAString& aLeaf) {
+ nsModuleHandle dll(::LoadLibraryW(PrependWorkingDir(aLeaf).get()));
+ EXPECT_TRUE(!!dll);
+ }
+
+ virtual void SetUp() override {
+ TelemetryTestFixture::SetUp();
+ ::InitOnceExecuteOnce(&sInitLoadOnce, InitialModuleLoadOnce, nullptr,
+ nullptr);
+ }
+
+ static const Vector<UntrustedModulesData>& GetInitLoadData() {
+ return sInitLoadDataCollector.Data();
+ }
+
+ // This method is useful if we want a new instance of UntrustedModulesData
+ // which is not copyable.
+ static UntrustedModulesData CollectSingleData() {
+ // If we call LoadAndFree more than once, those loading events are
+ // likely to be merged into an instance of UntrustedModulesData,
+ // meaning the length of the collector's vector is at least one but
+ // the exact number is unknown.
+ LoadAndFree(kTestModules[0]);
+
+ UntrustedModulesCollector collector;
+ ModuleLoadCounter waitForOne({kTestModules[0]}, {1});
+ EXPECT_NS_SUCCEEDED(collector.Collect(waitForOne));
+ EXPECT_TRUE(waitForOne.Remains({kTestModules[0]}, {0}));
+ EXPECT_EQ(collector.Data().length(), 1U);
+
+ // Cannot "return collector.Data()[0]" as copy ctor is deleted.
+ return UntrustedModulesData(std::move(collector.Data()[0]));
+ }
+
+ template <typename DataFetcherT>
+ void ValidateJSValue(const char16_t* aPattern, size_t aPatternLength,
+ DataFetcherT&& aDataFetcher) {
+ AutoJSContextWithGlobal cx(mCleanGlobal);
+ mozilla::Telemetry::UntrustedModulesDataSerializer serializer(
+ cx.GetJSContext(), kMaxModulesArrayLen);
+ EXPECT_TRUE(!!serializer);
+ aDataFetcher(serializer);
+
+ JS::Rooted<JS::Value> jsval(cx.GetJSContext());
+ serializer.GetObject(&jsval);
+
+ nsAutoString json;
+ EXPECT_TRUE(nsContentUtils::StringifyJSON(
+ cx.GetJSContext(), jsval, json, dom::UndefinedIsNullStringLiteral));
+
+ JS::Rooted<JSObject*> re(
+ cx.GetJSContext(),
+ JS::NewUCRegExpObject(cx.GetJSContext(), aPattern, aPatternLength,
+ JS::RegExpFlag::Global));
+ EXPECT_TRUE(!!re);
+
+ JS::Rooted<JS::Value> matchResult(cx.GetJSContext(), JS::NullValue());
+ size_t idx = 0;
+ EXPECT_TRUE(JS::ExecuteRegExpNoStatics(cx.GetJSContext(), re, json.get(),
+ json.Length(), &idx, true,
+ &matchResult));
+ // On match, with aOnlyMatch = true, ExecuteRegExpNoStatics returns boolean
+ // true. If no match, ExecuteRegExpNoStatics returns Null.
+ EXPECT_TRUE(matchResult.isBoolean() && matchResult.toBoolean());
+ if (!matchResult.isBoolean() || !matchResult.toBoolean()) {
+ // If match failed, print out the actual JSON kindly.
+ wprintf(L"JSON: %s\n", static_cast<const wchar_t*>(json.get()));
+ wprintf(L"RE: %s\n", aPattern);
+ }
+ }
+};
+
+const nsString UntrustedModulesFixture::kTestModules[] = {
+ // Sorted for binary-search
+ u"TestUntrustedModules_Dll1.dll"_ns,
+ u"TestUntrustedModules_Dll2.dll"_ns,
+};
+
+INIT_ONCE UntrustedModulesFixture::sInitLoadOnce = INIT_ONCE_STATIC_INIT;
+UntrustedModulesCollector UntrustedModulesFixture::sInitLoadDataCollector;
+
+void UntrustedModulesFixture::ValidateUntrustedModules(
+ const UntrustedModulesData& aData, bool aIsTruncatedData) {
+ // This defines a list of modules which are listed on our blocklist and
+ // thus its loading status is not expected to be Status::Loaded.
+ // Although the UntrustedModulesFixture test does not touch any of them,
+ // the current process might have run a test like TestDllBlocklist where
+ // we try to load and block them.
+ const struct {
+ const wchar_t* mName;
+ ModuleLoadInfo::Status mStatus;
+ } kKnownModules[] = {
+ // Sorted by mName for binary-search
+ {L"TestDllBlocklist_MatchByName.dll", ModuleLoadInfo::Status::Blocked},
+ {L"TestDllBlocklist_MatchByVersion.dll", ModuleLoadInfo::Status::Blocked},
+ {L"TestDllBlocklist_NoOpEntryPoint.dll",
+ ModuleLoadInfo::Status::Redirected},
+#if !defined(MOZ_ASAN)
+ // With ASAN, the test uses mozglue's blocklist where
+ // the user blocklist is not used. So only check for this
+ // DLL in the non-ASAN case.
+ {L"TestDllBlocklist_UserBlocked.dll", ModuleLoadInfo::Status::Blocked},
+#endif // !defined(MOZ_ASAN)
+ };
+
+ EXPECT_EQ(aData.mProcessType, GeckoProcessType_Default);
+ EXPECT_EQ(aData.mPid, ::GetCurrentProcessId());
+
+ nsTHashtable<nsPtrHashKey<void>> moduleSet;
+ for (const RefPtr<ModuleRecord>& module : aData.mModules.Values()) {
+ moduleSet.PutEntry(module);
+ }
+
+ size_t numBlockedEvents = 0;
+ for (auto item : aData.mEvents) {
+ const auto& evt = item->mEvent;
+ const nsDependentSubstring leafName =
+ nt::GetLeafName(evt.mModule->mResolvedNtName);
+ const nsAutoString leafNameStr(leafName.Data(), leafName.Length());
+ const ModuleLoadInfo::Status loadStatus =
+ static_cast<ModuleLoadInfo::Status>(evt.mLoadStatus);
+ if (loadStatus == ModuleLoadInfo::Status::Blocked) {
+ ++numBlockedEvents;
+ }
+
+ size_t match;
+ if (BinarySearchIf(
+ kKnownModules, 0, ArrayLength(kKnownModules),
+ [&leafNameStr](const auto& aVal) {
+ return _wcsicmp(leafNameStr.get(), aVal.mName);
+ },
+ &match)) {
+ EXPECT_EQ(loadStatus, kKnownModules[match].mStatus);
+ } else {
+ EXPECT_EQ(evt.mLoadStatus, 0U);
+ }
+
+ if (BinarySearchIf(
+ kTestModules, 0, ArrayLength(kTestModules),
+ [&leafNameStr](const auto& aVal) {
+ return _wcsicmp(leafNameStr.get(), aVal.get());
+ },
+ &match)) {
+ // We know the test modules are loaded in the main thread,
+ // but we don't know about other modules.
+ EXPECT_EQ(evt.mThreadId, ::GetCurrentThreadId());
+ }
+
+ // Make sure mModule is pointing to an entry of mModules.
+ EXPECT_TRUE(moduleSet.Contains(evt.mModule));
+ EXPECT_FALSE(evt.mIsDependent);
+ }
+
+ // No check for the mXULLoadDurationMS field because the field has a value
+ // in CCov build GTest, but it is empty in non-CCov build (bug 1681936).
+ EXPECT_EQ(aData.mNumEvents, aData.mEvents.length());
+ EXPECT_GT(aData.mNumEvents, 0U);
+ if (aIsTruncatedData) {
+ EXPECT_EQ(aData.mStacks.GetModuleCount(), 0U);
+ EXPECT_LE(aData.mNumEvents, UntrustedModulesData::kMaxEvents);
+ } else if (numBlockedEvents == aData.mNumEvents) {
+ // If all loading events were blocked or aData is truncated,
+ // the stacks are empty.
+ EXPECT_EQ(aData.mStacks.GetModuleCount(), 0U);
+ } else {
+ EXPECT_GT(aData.mStacks.GetModuleCount(), 0U);
+ }
+ EXPECT_EQ(aData.mSanitizationFailures, 0U);
+ EXPECT_EQ(aData.mTrustTestFailures, 0U);
+}
+
+BOOL CALLBACK UntrustedModulesFixture::InitialModuleLoadOnce(PINIT_ONCE, void*,
+ void**) {
+ for (int i = 0; i < kLoadCountBeforeDllServices; ++i) {
+ for (const auto& mod : kTestModules) {
+ LoadAndFree(mod);
+ }
+ }
+
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->StartUntrustedModulesProcessor(true);
+
+ for (int i = 0; i < kLoadCountAfterDllServices; ++i) {
+ for (const auto& mod : kTestModules) {
+ LoadAndFree(mod);
+ }
+ }
+
+ ModuleLoadCounter waitForTwo(kTestModules, {kInitLoadCount, kInitLoadCount});
+ EXPECT_EQ(sInitLoadDataCollector.Collect(waitForTwo), NS_OK);
+ EXPECT_TRUE(waitForTwo.Remains(kTestModules, {0, 0}));
+
+ for (const auto& event : GetInitLoadData()) {
+ ValidateUntrustedModules(event);
+ }
+
+ // Data was removed when retrieved. No data is retrieved again.
+ UntrustedModulesCollector collector;
+ ModuleLoadCounter waitOnceForEach(kTestModules, {1, 1});
+ EXPECT_EQ(collector.Collect(waitOnceForEach), NS_ERROR_ABORT);
+ EXPECT_TRUE(waitOnceForEach.Remains(kTestModules, {1, 1}));
+
+ return TRUE;
+}
+
+#define PROCESS_OBJ(TYPE, PID) \
+ u"\"" TYPE u"\\." PID u"\":{" \
+ u"\"processType\":\"" TYPE u"\",\"elapsed\":\\d+\\.\\d+," \
+ u"\"sanitizationFailures\":0,\"trustTestFailures\":0," \
+ u"\"events\":\\[{" \
+ u"\"processUptimeMS\":\\d+,\"loadDurationMS\":\\d+\\.\\d+," \
+ u"\"threadID\":\\d+,\"threadName\":\"Main Thread\"," \
+ u"\"baseAddress\":\"0x[0-9a-f]+\",\"moduleIndex\":0," \
+ u"\"isDependent\":false,\"loadStatus\":0}\\]," \
+ u"\"combinedStacks\":{" \
+ u"\"memoryMap\":\\[\\[\"\\w+\\.\\w+\",\"[0-9A-Z]+\"\\]" \
+ u"(,\\[\"\\w+\\.\\w+\",\"[0-9A-Z]+\\\"\\])*\\]," \
+ u"\"stacks\":\\[\\[\\[(-1|\\d+),\\d+\\]" \
+ u"(,\\[(-1|\\d+),\\d+\\])*\\]\\]}}"
+
+TEST_F(UntrustedModulesFixture, Serialize) {
+ // clang-format off
+ const char16_t kPattern[] = u"{\"structVersion\":1,"
+ u"\"modules\":\\[{"
+ u"\"resolvedDllName\":\"TestUntrustedModules_Dll1\\.dll\","
+ u"\"fileVersion\":\"1\\.2\\.3\\.4\","
+ // It would be nice to hard-code this, but this might change with
+ // compiler versions, etc.
+ u"\"debugID\":\"[0-9A-F]{33}\","
+ u"\"companyName\":\"Mozilla Corporation\",\"trustFlags\":0}\\],"
+ u"\"blockedModules\":\\[.*?\\]," // allow for the case where there are some blocked modules
+ u"\"processes\":{"
+ PROCESS_OBJ(u"browser", u"0xabc") u","
+ PROCESS_OBJ(u"browser", u"0x4") u","
+ PROCESS_OBJ(u"rdd", u"0x4")
+ u"}}";
+ // clang-format on
+
+ UntrustedModulesBackupData backup1, backup2;
+ {
+ UntrustedModulesData data1 = CollectSingleData();
+ UntrustedModulesData data2 = CollectSingleData();
+ UntrustedModulesData data3 = CollectSingleData();
+
+ data1.mPid = 0xabc;
+ data2.mPid = 0x4;
+ data2.mProcessType = GeckoProcessType_RDD;
+ data3.mPid = 0x4;
+
+ backup1.Add(std::move(data1));
+ backup2.Add(std::move(data2));
+ backup1.Add(std::move(data3));
+ }
+
+ ValidateJSValue(kPattern, ArrayLength(kPattern) - 1,
+ [&backup1, &backup2](
+ Telemetry::UntrustedModulesDataSerializer& aSerializer) {
+ EXPECT_NS_SUCCEEDED(aSerializer.Add(backup1));
+ EXPECT_NS_SUCCEEDED(aSerializer.Add(backup2));
+ });
+}
+
+TEST_F(UntrustedModulesFixture, Backup) {
+ RefPtr<UntrustedModulesBackupService> backupSvc(
+ UntrustedModulesBackupService::Get());
+ for (int i = 0; i < 100; ++i) {
+ backupSvc->Backup(CollectSingleData());
+ }
+
+ backupSvc->SettleAllStagingData();
+ EXPECT_TRUE(backupSvc->Staging().IsEmpty());
+
+ for (const auto& entry : backupSvc->Settled()) {
+ const RefPtr<UntrustedModulesDataContainer>& container = entry.GetData();
+ EXPECT_TRUE(!!container);
+ const UntrustedModulesData& data = container->mData;
+ EXPECT_EQ(entry.GetKey(), ProcessHashKey(data.mProcessType, data.mPid));
+ ValidateUntrustedModules(data, /*aIsTruncatedData*/ true);
+ }
+}
diff --git a/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp
new file mode 100644
index 0000000000..4f6ce877eb
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc
new file mode 100644
index 0000000000..2358b88b93
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/TestUntrustedModules_Dll1.rc
@@ -0,0 +1,38 @@
+/* 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 <winver.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,2,3,4 // This field will be collected
+ PRODUCTVERSION 5,6,7,8
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "Mozilla Corporation"
+ VALUE "OriginalFilename", "TestUntrustedModules_Dll1.dll"
+ VALUE "ProductName", "Test DLL"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/moz.build b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/moz.build
new file mode 100644
index 0000000000..57fc59ca8a
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll1/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestUntrustedModules_Dll1")
+
+UNIFIED_SOURCES = [
+ "TestUntrustedModules_Dll1.cpp",
+]
+
+RCFILE = "TestUntrustedModules_Dll1.rc"
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestUntrustedModules_Dll1.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp
new file mode 100644
index 0000000000..4f6ce877eb
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/TestUntrustedModules_Dll2.cpp
@@ -0,0 +1,7 @@
+/* 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>
+
+BOOL WINAPI DllMain(HINSTANCE, DWORD, LPVOID) { return TRUE; }
diff --git a/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/moz.build b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/moz.build
new file mode 100644
index 0000000000..fcefe41329
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/TestUntrustedModules_Dll2/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+DIST_INSTALL = False
+
+SharedLibrary("TestUntrustedModules_Dll2")
+
+UNIFIED_SOURCES = [
+ "TestUntrustedModules_Dll2.cpp",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_HARNESS_FILES.gtest += ["!TestUntrustedModules_Dll2.dll"]
diff --git a/toolkit/xre/dllservices/tests/gtest/moz.build b/toolkit/xre/dllservices/tests/gtest/moz.build
new file mode 100644
index 0000000000..92f341841a
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/moz.build
@@ -0,0 +1,39 @@
+# 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/.
+
+Library("dllservicestest")
+
+UNIFIED_SOURCES += [
+ "TestDLLBlocklist.cpp",
+ "TestUntrustedModules.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/toolkit/components/telemetry/other",
+ "/toolkit/components/telemetry/tests/gtest",
+]
+
+TEST_DIRS += [
+ "rust",
+ "TestDllBlocklist_AllowByVersion",
+ "TestDllBlocklist_GMPluginProcessOnly",
+ "TestDllBlocklist_GPUProcessOnly",
+ "TestDllBlocklist_MatchByName",
+ "TestDllBlocklist_MatchByVersion",
+ "TestDllBlocklist_MultipleEntries_DifferentProcesses",
+ "TestDllBlocklist_MultipleEntries_SameProcessBackward",
+ "TestDllBlocklist_MultipleEntries_SameProcessForward",
+ "TestDllBlocklist_NoOpEntryPoint",
+ "TestDllBlocklist_SingleNotification1",
+ "TestDllBlocklist_SingleNotification2",
+ "TestDllBlocklist_SocketProcessOnly",
+ "TestDllBlocklist_UserBlocked",
+ "TestDllBlocklist_UtilityProcessOnly",
+ "TestUntrustedModules_Dll1",
+ "TestUntrustedModules_Dll2",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/toolkit/xre/dllservices/tests/gtest/rust/Cargo.toml b/toolkit/xre/dllservices/tests/gtest/rust/Cargo.toml
new file mode 100644
index 0000000000..a342fb8bf2
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/rust/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "dllservices-gtest"
+version = "0.1.0"
+authors = ["nobody@mozilla.com"]
+license = "MPL-2.0"
+description = "Tests for dllservices"
+
+[dependencies]
+uuid = { version = "1.0", features = ["v4"] }
+
+[lib]
+path = "test.rs"
diff --git a/toolkit/xre/dllservices/tests/gtest/rust/TestBCryptFallback.cpp b/toolkit/xre/dllservices/tests/gtest/rust/TestBCryptFallback.cpp
new file mode 100644
index 0000000000..ca4dbde3ab
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/rust/TestBCryptFallback.cpp
@@ -0,0 +1,113 @@
+/* -*- 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 <windows.h>
+#include <ntstatus.h>
+
+#include <bcrypt.h>
+#pragma comment(lib, "bcrypt.lib")
+
+#include "gtest/gtest.h"
+
+#include "nsWindowsDllInterceptor.h"
+
+#define RtlGenRandom SystemFunction036
+extern "C" BOOLEAN NTAPI RtlGenRandom(PVOID aRandomBuffer,
+ ULONG aRandomBufferLength);
+
+static mozilla::WindowsDllInterceptor BCryptIntercept;
+static mozilla::WindowsDllInterceptor::FuncHookType<
+ decltype(&::BCryptGenRandom)>
+ stub_BCryptGenRandom;
+
+static mozilla::WindowsDllInterceptor AdvApiIntercept;
+static mozilla::WindowsDllInterceptor::FuncHookType<decltype(&RtlGenRandom)>
+ stub_RtlGenRandom;
+
+volatile bool gAreHooksActive = false;
+volatile bool gHasPanicked = false;
+volatile bool gHasReachedBCryptGenRandom = false;
+volatile bool gHasReachedRtlGenRandom = false;
+
+NTSTATUS WINAPI patched_BCryptGenRandom(BCRYPT_ALG_HANDLE aAlgorithm,
+ PUCHAR aBuffer, ULONG aSize,
+ ULONG aFlags) {
+ if (gAreHooksActive) {
+ gHasReachedBCryptGenRandom = true;
+ // Force BCryptGenRandom failures when the hook is active.
+ return STATUS_UNSUCCESSFUL;
+ }
+ return stub_BCryptGenRandom(aAlgorithm, aBuffer, aSize, aFlags);
+}
+
+BOOLEAN NTAPI patched_RtlGenRandom(PVOID aRandomBuffer,
+ ULONG aRandomBufferLength) {
+ if (gAreHooksActive) {
+ gHasReachedRtlGenRandom = true;
+ }
+ return stub_RtlGenRandom(aRandomBuffer, aRandomBufferLength);
+}
+
+bool InitInterception() {
+ static bool sSuccess = []() {
+ BCryptIntercept.Init(L"bcrypt.dll");
+ AdvApiIntercept.Init(L"advapi32.dll");
+ return stub_BCryptGenRandom.SetDetour(BCryptIntercept, "BCryptGenRandom",
+ patched_BCryptGenRandom) &&
+ stub_RtlGenRandom.SetDetour(AdvApiIntercept, "SystemFunction036",
+ patched_RtlGenRandom);
+ }();
+ gAreHooksActive = true;
+ return sSuccess;
+}
+
+void ExitInterception() { gAreHooksActive = false; }
+
+DWORD WINAPI TestIsFallbackTriggeredThreadProc(LPVOID aParameter) {
+ auto testedFunction = reinterpret_cast<void (*)()>(aParameter);
+ EXPECT_TRUE(InitInterception());
+ MOZ_SEH_TRY { testedFunction(); }
+ MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ // Catch a potential Rust panic
+ gHasPanicked = true;
+ }
+ ExitInterception();
+ return 0;
+}
+
+// This function hooks BCryptGenRandom to make it fail, and hooks RtlGenRandom
+// to allow us to ensure that it gets visited as a fallback for
+// BCryptGenRandom.
+void TestIsFallbackTriggered(void (*aTestedFunction)()) {
+ gHasPanicked = false;
+ gHasReachedBCryptGenRandom = false;
+ gHasReachedRtlGenRandom = false;
+
+ // The HashMap test must run on a new thread, because some random bytes have
+ // already been collected but not used on the current thread by previous
+ // calls to HashMap::new in various locations of the code base. These bytes
+ // would be recycled instead of calling into BCryptGenRandom and RtlGenRandom
+ // if running the HashMap test on the current thread.
+ auto thread =
+ ::CreateThread(nullptr, 0, TestIsFallbackTriggeredThreadProc,
+ reinterpret_cast<void*>(aTestedFunction), 0, nullptr);
+ EXPECT_TRUE(bool(thread));
+ EXPECT_EQ(::WaitForSingleObject(thread, 5000),
+ static_cast<DWORD>(WAIT_OBJECT_0));
+
+ EXPECT_FALSE(gHasPanicked);
+ EXPECT_TRUE(gHasReachedBCryptGenRandom);
+ EXPECT_TRUE(gHasReachedRtlGenRandom);
+}
+
+extern "C" void Rust_TriggerGenRandomFromHashMap();
+extern "C" void Rust_TriggerGenRandomFromUuid();
+
+TEST(TestBCryptFallback, HashMapTriggersFallback)
+{ TestIsFallbackTriggered(Rust_TriggerGenRandomFromHashMap); }
+
+TEST(TestBCryptFallback, UuidTriggersFallback)
+{ TestIsFallbackTriggered(Rust_TriggerGenRandomFromUuid); }
diff --git a/toolkit/xre/dllservices/tests/gtest/rust/moz.build b/toolkit/xre/dllservices/tests/gtest/rust/moz.build
new file mode 100644
index 0000000000..eea8b9e978
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/rust/moz.build
@@ -0,0 +1,11 @@
+# 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/.
+
+Library("dllservicestest")
+
+UNIFIED_SOURCES += [
+ "TestBCryptFallback.cpp",
+]
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/toolkit/xre/dllservices/tests/gtest/rust/test.rs b/toolkit/xre/dllservices/tests/gtest/rust/test.rs
new file mode 100644
index 0000000000..51c2a6e156
--- /dev/null
+++ b/toolkit/xre/dllservices/tests/gtest/rust/test.rs
@@ -0,0 +1,19 @@
+/* -*- Mode: rust; rust-indent-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/. */
+
+use std::collections::HashMap;
+
+extern crate uuid;
+use uuid::Uuid;
+
+#[no_mangle]
+pub extern "C" fn Rust_TriggerGenRandomFromHashMap() -> () {
+ let _: HashMap<u32, u32> = HashMap::new();
+}
+
+#[no_mangle]
+pub extern "C" fn Rust_TriggerGenRandomFromUuid() -> () {
+ let _: Uuid = Uuid::new_v4();
+}