--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -9,32 +9,36 @@
#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
// See mozmemory_wrap.h for more details. This file is part of libmozglue, so
// it needs to use _impl suffixes.
#define MALLOC_DECL(name, return_type, ...) \
extern "C" MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__);
#include "malloc_decls.h"
#endif
+#include <ntstatus.h>
+#define WIN32_NO_STATUS // Because we've explicitly including ntstatus.h
+
#include <windows.h>
#include <winternl.h>
#include <io.h>
#pragma warning( push )
#pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h
#include <map>
#pragma warning( pop )
#include "nsAutoPtr.h"
#include "nsWindowsDllInterceptor.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WindowsVersion.h"
#include "nsWindowsHelpers.h"
#include "WindowsDllBlocklist.h"
+#include "WindowsLSPPassthrough.h"
using namespace mozilla;
#define ALL_VERSIONS ((unsigned long long)-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
@@ -64,16 +68,22 @@ struct DllBlockInfo {
// the IMAGE_FILE_HEADER in lieu of a version number.
unsigned long long maxVersion;
enum {
FLAGS_DEFAULT = 0,
BLOCK_WIN8PLUS_ONLY = 1,
BLOCK_XP_ONLY = 2,
USE_TIMESTAMP = 4,
+ /**
+ * IMPORTANT: If SUBSTITUTE_LSP_PASSTHROUGH is specified, the LSP's
+ * Protocol GUID must also be added to the gLayerGuids array in
+ * WindowsLSPPassthough.cpp!
+ */
+ SUBSTITUTE_LSP_PASSTHROUGH = 8,
} flags;
};
static DllBlockInfo sWindowsDllBlocklist[] = {
// EXAMPLE:
// { "uxtheme.dll", ALL_VERSIONS },
// { "uxtheme.dll", 0x0000123400000000ULL },
// The DLL name must be in lowercase!
@@ -203,16 +213,22 @@ static DllBlockInfo sWindowsDllBlocklist
// SS2OSD, bug 1262348
{ "ss2osd.dll", ALL_VERSIONS },
{ "ss2devprops.dll", ALL_VERSIONS },
// NHASUSSTRIXOSD.DLL, bug 1269244
{ "nhasusstrixosd.dll", ALL_VERSIONS },
{ "nhasusstrixdevprops.dll", ALL_VERSIONS },
+
+ // LSPs for unit testing
+ // IMPORTANT: Do not specify SUBSTITUTE_LSP_PASSTHROUGH without adding
+ // the LSP's Protocol GUID to gLayerGuids in WindowsLSPPassthrough.cpp!
+ { "testlsp.dll", ALL_VERSIONS, DllBlockInfo::SUBSTITUTE_LSP_PASSTHROUGH },
+ { "testlsp2.dll", ALL_VERSIONS, DllBlockInfo::SUBSTITUTE_LSP_PASSTHROUGH },
// Crashes with PremierOpinion/RelevantKnowledge, bug 1277846
{ "opls.dll", ALL_VERSIONS },
{ "opls64.dll", ALL_VERSIONS },
{ "pmls.dll", ALL_VERSIONS },
{ "pmls64.dll", ALL_VERSIONS },
{ "prls.dll", ALL_VERSIONS },
{ "prls64.dll", ALL_VERSIONS },
@@ -663,22 +679,22 @@ patched_LdrLoadDll (PWCHAR filePath, PUL
if (info->name) {
bool load_ok = false;
#ifdef DEBUG_very_verbose
printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
#endif
- if ((info->flags == DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
+ if ((info->flags & DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
!IsWin8OrLater()) {
goto continue_loading;
}
- if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) &&
+ if ((info->flags & DllBlockInfo::BLOCK_XP_ONLY) &&
IsWin2003OrLater()) {
goto continue_loading;
}
unsigned long long fVersion = ALL_VERSIONS;
if (info->maxVersion != ALL_VERSIONS) {
ReentrancySentinel sentinel(dllName);
@@ -721,16 +737,50 @@ patched_LdrLoadDll (PWCHAR filePath, PUL
if (fVersion > info->maxVersion)
load_ok = true;
}
}
}
}
if (!load_ok) {
+ if (info->flags & DllBlockInfo::SUBSTITUTE_LSP_PASSTHROUGH) {
+ if (!full_fname) {
+ full_fname = getFullPath(filePath, fname);
+ if (!full_fname) {
+ // uh, we couldn't find the DLL at all, so...
+ printf_stderr("LdrLoadDll: SearchPathW didn't find DLL '%s', unable to substitute LSP\n", dllName);
+ return STATUS_DLL_NOT_FOUND;
+ }
+ }
+
+ if (!mozilla::lsp::IsRegisteredBlockedLSP(full_fname.get())) {
+ // We want to block this LSP, however this DLL does not match any
+ // registered LSP layer providers whose GUIDs are blocked!
+ // Failing to allow the DLL load under these conditions will likely
+ // break Winsock, so we must allow the LSP to load.
+ goto continue_loading;
+ }
+
+ printf_stderr("LdrLoadDll: Blocking LSP load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
+ DllBlockSet::Add(info->name, fVersion);
+
+ // We substitute our own module (ie, the one containing this function)
+ // for the LSP's module. We also pin this module's reference count so
+ // that nobody can accidentally FreeLibrary() us.
+ HMODULE ourModule = NULL;
+ if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN |
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+ reinterpret_cast<LPCWSTR>(&patched_LdrLoadDll),
+ &ourModule)) {
+ *handle = ourModule;
+ return STATUS_SUCCESS;
+ }
+ }
+
printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
DllBlockSet::Add(info->name, fVersion);
return STATUS_DLL_NOT_FOUND;
}
}
continue_loading:
#ifdef DEBUG_very_verbose
new file mode 100644
--- /dev/null
+++ b/mozglue/build/WindowsLSPPassthrough.cpp
@@ -0,0 +1,602 @@
+/* -*- 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/. */
+
+#ifdef MOZ_MEMORY
+#define MOZ_MEMORY_IMPL
+#include "mozmemory_wrap.h"
+#define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
+// See mozmemory_wrap.h for more details. This file is part of libmozglue, so
+// it needs to use _impl suffixes.
+#define MALLOC_DECL(name, return_type, ...) \
+ extern "C" MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__);
+#include "malloc_decls.h"
+#endif
+
+#define __INLINE_ISEQUAL_GUID
+#define _WINSOCKAPI_ // Prevent windows.h from including winsock 1.0 headers
+#include <windows.h>
+#undef _WINSOCKAPI_
+#include <winsock2.h>
+#include <ws2spi.h>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Move.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Vector.h"
+#include "nsWindowsHelpers.h"
+
+/**
+ * IMPORTANT: How the passthrough LSP knows which LSP it is substituting for:
+ *
+ * winsock2 is architected such that its API is exposed by ws2_32.dll but is
+ * actually implemented in another DLL. This implementation DLL is called the
+ * "Base Protocol."
+ *
+ * In addition, winsock2 permits various filter DLLs to be installed between
+ * ws2_32.dll and its Base Protocol. These filter DLLs are called "Layered
+ * Protocols." Layered Protocols are the LSPs that we are interested in
+ * substituting.
+ *
+ * Since normally the Layered Protocols know who they are, winsock2 does not
+ * provide that information when it calls back into the DLL. In our case, we can
+ * substitute for any Layered Protocol DLL. Since winsock2 does not tell us
+ * for whom we are substituting, we must figure that out ourselves. This is
+ * important because we are responsible for loading the next layer in the stack.
+ *
+ * We learn who we are supposed to be by obtaining the list of registered LSPs
+ * and searching it for the GUID of a blocked LSP. Once we have found that GUID,
+ * we have found the LSP that we are substituting for.
+ *
+ * Further note that, unlike normal LSPs, it is possible (however unlikely)
+ * that a user might have multiple LSPs installed that we want to substitute. We
+ * use the same technique in this case as well, but we count the number of times
+ * that our WSPStartup has been reentered. If it has been reentered N times,
+ * then the LSP we are substituting for is the Nth blocked GUID found in the
+ * protocol search.
+ */
+
+namespace {
+
+const GUID gLayerGuids[] = {
+ // testlsp GUID. This must match gTestLspGuid declared in TestDllBlocklist.cpp
+ { 0x9414bc49, 0x7f64, 0x4a70, { 0x98, 0xc2, 0xba, 0x0, 0xec, 0xe, 0x43, 0x60 } },
+ // testlsp2 GUID. This must match gTestLsp2Guid declared in TestDllBlocklist.cpp
+ { 0x6fc98c7c, 0x48ee, 0x41a0, { 0xa3, 0xa4, 0x7b, 0x9e, 0x9f, 0x39, 0xb7, 0xb6 } }
+};
+
+class MOZ_RAII ProtocolList
+{
+public:
+ ProtocolList()
+ : mCount(0)
+ {
+ DWORD size;
+ INT error;
+ int result = ::WSCEnumProtocols(nullptr, nullptr, &size, &error);
+ if (result == SOCKET_ERROR && error != WSAENOBUFS) {
+ return;
+ }
+ int count = size / sizeof(WSAPROTOCOL_INFOW);
+ MOZ_ASSERT(size % sizeof(WSAPROTOCOL_INFOW) == 0);
+ auto providerData = mozilla::MakeUnique<WSAPROTOCOL_INFOW[]>(count);
+ // Make sure that if the calculation of count rounded down (it shouldn't
+ // have, but...) that we have adjusted size accordingly to avoid overrun.
+ size = count * sizeof(WSAPROTOCOL_INFOW);
+ count = WSCEnumProtocols(nullptr, providerData.get(), &size, &error);
+ if (count == SOCKET_ERROR) {
+ return;
+ }
+ mProtocolData = mozilla::Move(providerData);
+ mCount = count;
+ }
+
+ WSAPROTOCOL_INFOW*
+ Find(DWORD aCatId)
+ {
+ for (int i = 0; i < mCount; ++i) {
+ if (mProtocolData[i].dwCatalogEntryId == aCatId) {
+ return &mProtocolData[i];
+ }
+ }
+ return nullptr;
+ }
+
+ WSAPROTOCOL_INFOW*
+ FindLayerNamed(const wchar_t* aFullyQualifiedFileName)
+ {
+ for (int i = 0; i < mCount; ++i) {
+ WSAPROTOCOL_INFOW& info = mProtocolData[i];
+ if (info.ProtocolChain.ChainLen != LAYERED_PROTOCOL) {
+ continue;
+ }
+
+ wchar_t provPath[MAX_PATH + 1] = {};
+ INT provPathLen = mozilla::ArrayLength(provPath);
+ INT errorCode;
+ int status = ::WSCGetProviderPath(&info.ProviderId, provPath,
+ &provPathLen, &errorCode);
+ if (status) {
+ continue;
+ }
+
+ DWORD expandedProvPathLen = ::ExpandEnvironmentStringsW(provPath, nullptr,
+ 0);
+ if (!expandedProvPathLen) {
+ continue;
+ }
+
+ auto expandedProvPath = mozilla::MakeUnique<wchar_t[]>(expandedProvPathLen);
+ DWORD expandResult = ::ExpandEnvironmentStringsW(provPath,
+ expandedProvPath.get(),
+ expandedProvPathLen);
+ if (expandResult != expandedProvPathLen) {
+ continue;
+ }
+
+ if (!_wcsnicmp(aFullyQualifiedFileName,
+ expandedProvPath.get(), expandedProvPathLen)) {
+ return &info;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+ ProtocolList(const ProtocolList&) = delete;
+ ProtocolList& operator=(const ProtocolList&) = delete;
+
+ int mCount;
+ mozilla::UniquePtr<WSAPROTOCOL_INFOW[]> mProtocolData;
+};
+
+class MOZ_RAII ReentryCounter
+{
+public:
+ ReentryCounter()
+ {
+ MOZ_ASSERT(sReentryCount >= 0);
+ ++sReentryCount;
+ }
+
+ ~ReentryCounter()
+ {
+ --sReentryCount;
+ MOZ_ASSERT(sReentryCount >= 0);
+ }
+
+ static int GetCount()
+ {
+ return sReentryCount;
+ }
+
+private:
+ ReentryCounter(const ReentryCounter&) = delete;
+ ReentryCounter& operator=(const ReentryCounter&) = delete;
+
+ static int sReentryCount;
+};
+
+int ReentryCounter::sReentryCount = 0;
+
+bool IsGuidBlocked(REFGUID aGuid)
+{
+ for (unsigned int i = 0; i < mozilla::ArrayLength(gLayerGuids); ++i) {
+ if (aGuid == gLayerGuids[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// We need to follow chain entries all the way to their layer catalog entry.
+// This is basically a depth-first search using the catalog id as a reference
+// to the next node in the graph.
+bool IsProviderBlocked(ProtocolList& aProtocolList, DWORD aCatalogId,
+ int& aBlockCount, int& aChainIndex)
+{
+ WSAPROTOCOL_INFOW* catEntry = aProtocolList.Find(aCatalogId);
+ if (!catEntry) {
+ return false;
+ }
+ // If we've reached a base protocol node, return false; we don't block those
+ if (catEntry->ProtocolChain.ChainLen == BASE_PROTOCOL) {
+ return false;
+ }
+ // If we've reached a layer node, check the GUID against the blocklist
+ if (catEntry->ProtocolChain.ChainLen == LAYERED_PROTOCOL) {
+ if (IsGuidBlocked(catEntry->ProviderId)) {
+ ++aBlockCount;
+ // Since we have reentered N times, our 'this' catalog entry will
+ // correspond to the Nth blocked entry. Otherwise we keep searching.
+ if (aBlockCount == ReentryCounter::GetCount()) {
+ aChainIndex = 0;
+ return true;
+ }
+ }
+ return false;
+ }
+ // Otherwise we need to walk the next chain
+ WSAPROTOCOLCHAIN& chain = catEntry->ProtocolChain;
+ for (int i = 0; i < chain.ChainLen; ++i) {
+ int chainIndex = 0; // chainIndex is just a dummy placeholder
+ if (IsProviderBlocked(aProtocolList, chain.ChainEntries[i], aBlockCount,
+ chainIndex)) {
+ // We save the index into the top-level chain before returning
+ aChainIndex = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+int GetNextChainIndex(ProtocolList& aProtocolList, DWORD aCatalogId)
+{
+ int blockCount = 0;
+ int chainIndex = 0;
+ if (!IsProviderBlocked(aProtocolList, aCatalogId, blockCount, chainIndex)) {
+ // This shouldn't be possible because our LSP is by definition not a
+ // base provider -- there must be at least one protocol chain.
+ MOZ_ASSERT(false);
+ return MAX_PROTOCOL_CHAIN;
+ }
+ return chainIndex + 1;
+}
+
+class Mutex
+{
+public:
+ Mutex()
+ {
+ const DWORD kNSPRSpinCount = 1500;
+ ::InitializeCriticalSectionAndSpinCount(&mCS, kNSPRSpinCount);
+ }
+
+ ~Mutex()
+ {
+ ::DeleteCriticalSection(&mCS);
+ }
+
+private:
+ void Enter()
+ {
+ ::EnterCriticalSection(&mCS);
+ }
+
+ void Leave()
+ {
+ ::LeaveCriticalSection(&mCS);
+ }
+
+ Mutex(const Mutex&) = delete;
+ Mutex& operator=(const Mutex&) = delete;
+
+ CRITICAL_SECTION mCS;
+
+ friend class CriticalRegion;
+};
+
+class MOZ_RAII CriticalRegion
+{
+public:
+ explicit CriticalRegion(Mutex& aMutex)
+ : mMutex(aMutex)
+ {
+ aMutex.Enter();
+ }
+
+ ~CriticalRegion()
+ {
+ mMutex.Leave();
+ }
+
+private:
+ CriticalRegion(const CriticalRegion&) = delete;
+ CriticalRegion& operator=(const CriticalRegion&) = delete;
+
+ Mutex& mMutex;
+};
+
+class LSP
+{
+public:
+ LSP(const wchar_t* aLibraryPath, const WSAPROTOCOL_INFOW& aProtocolInfo)
+ : mNextLSP(::LoadLibraryW(aLibraryPath))
+ , mNextLSPStartupFn(nullptr)
+ , mNextLSPCleanupFn(nullptr)
+ {
+ if (!mNextLSP) {
+ return;
+ }
+ mNextLSPStartupFn = (LPWSPSTARTUP)::GetProcAddress(mNextLSP, "WSPStartup");
+ if (!mNextLSPStartupFn) {
+ return;
+ }
+ ZeroMemory(&mNextLSPData, sizeof(WSPDATA));
+ memcpy(&mNextLSPProtocolInfo, &aProtocolInfo, sizeof(WSAPROTOCOL_INFOW));
+ }
+
+ static int
+ TryStartup(WORD aVersionRequested,
+ LPWSPDATA aWSPData,
+ LPWSAPROTOCOL_INFOW aProtocolInfo,
+ WSPUPCALLTABLE aUpcallTable,
+ LPWSPPROC_TABLE aProcTable);
+
+ int Cleanup(INT& aErrno)
+ {
+ MOZ_ASSERT(mNextLSPCleanupFn);
+ return mNextLSPCleanupFn(&aErrno);
+ }
+
+private:
+ int
+ Startup(WORD aVersionRequested, LPWSPDATA aData, WSPUPCALLTABLE& aUpcallTable,
+ LPWSPPROC_TABLE aProcTable)
+ {
+ int result = mNextLSPStartupFn(aVersionRequested, &mNextLSPData,
+ &mNextLSPProtocolInfo, aUpcallTable,
+ aProcTable);
+ if (result) {
+ return result;
+ }
+ // We'll populate our own aData with some of the same info as the next
+ // entry in the chain
+ ZeroMemory(aData, sizeof(WSPDATA));
+ aData->wVersion = mNextLSPData.wVersion;
+ aData->wHighVersion = mNextLSPData.wHighVersion;
+ // Save the LSP's cleanup function and substitute ours for upstream
+ mNextLSPCleanupFn = aProcTable->lpWSPCleanup;
+ aProcTable->lpWSPCleanup = &WSPCleanup;
+ return 0;
+ }
+
+ inline bool
+ ok() const
+ {
+ return !!mNextLSPStartupFn;
+ }
+
+ nsModuleHandle mNextLSP;
+ WSAPROTOCOL_INFOW mNextLSPProtocolInfo;
+ LPWSPSTARTUP mNextLSPStartupFn;
+ LPWSPCLEANUP mNextLSPCleanupFn;
+ WSPDATA mNextLSPData;
+
+ static Mutex sMutex;
+
+ static int WSPAPI WSPCleanup(LPINT aErrno);
+
+ static mozilla::UniquePtr<LSP>
+ VerifyProtocolInfoAndLoadNextLSP(WSPUPCALLTABLE& aUpcallTable,
+ LPWSAPROTOCOL_INFOW aProtocolInfo)
+ {
+ if (aProtocolInfo->ProtocolChain.ChainLen < BASE_PROTOCOL) {
+ // We should not be seeing info for a base provider
+ return nullptr;
+ }
+ ProtocolList protocolList;
+ // Find the entry in the protocol chain that corresponds to us
+ int nextChainIndex = GetNextChainIndex(protocolList,
+ aProtocolInfo->dwCatalogEntryId);
+ if (nextChainIndex >= aProtocolInfo->ProtocolChain.ChainLen) {
+ // Uh-oh, nextChainIndex is out of bounds! This shouldn't be possible
+ // for layer providers.
+ return nullptr;
+ }
+ if (nextChainIndex == MAX_PROTOCOL_CHAIN) {
+ // We didn't find the next link in the chain, something is wrong
+ return nullptr;
+ }
+ DWORD nextChainId = aProtocolInfo->ProtocolChain.ChainEntries[nextChainIndex];
+ bool isNextLinkBase = nextChainIndex == aProtocolInfo->ProtocolChain.ChainLen - 1;
+ // Find the GUID for the provider corresponding to nextChainId
+ WSAPROTOCOL_INFOW* useProvider = protocolList.Find(nextChainId);
+ if (!useProvider) {
+ return nullptr;
+ }
+ INT error;
+ wchar_t provPath[MAX_PATH + 1] = {0};
+ INT provPathLen = MAX_PATH;
+ int result = aUpcallTable.lpWPUGetProviderPath(&useProvider->ProviderId,
+ provPath, &provPathLen,
+ &error);
+ if (result == SOCKET_ERROR) {
+ return nullptr;
+ }
+ DWORD desiredLen = ExpandEnvironmentStringsW(provPath, nullptr, 0);
+ if (!desiredLen) {
+ return nullptr;
+ }
+ auto expandedProvPath = mozilla::MakeUnique<wchar_t[]>(desiredLen);
+ DWORD expandedLen = ExpandEnvironmentStringsW(provPath,
+ expandedProvPath.get(),
+ desiredLen);
+ if (!expandedLen || expandedLen > desiredLen) {
+ return nullptr;
+ }
+ /* Subtle: If the next LSP in the chain is a base provider, we should specify
+ its base WSAPROTOCOL_INFOW, not the chain. OTOH we *do* need to
+ supply the dwProviderReserved field that was included with the
+ chain. */
+ if (isNextLinkBase) {
+ useProvider->dwProviderReserved = aProtocolInfo->dwProviderReserved;
+ } else {
+ useProvider = aProtocolInfo;
+ }
+ return mozilla::MakeUnique<LSP>(expandedProvPath.get(), *useProvider);
+ }
+};
+
+Mutex LSP::sMutex;
+
+struct LSPInfo
+{
+ LSPInfo()
+ : mRefCnt(0)
+ {
+ }
+
+ explicit LSPInfo(mozilla::UniquePtr<LSP> aLsp)
+ : mLsp(mozilla::Move(aLsp))
+ , mRefCnt(1)
+ {
+ }
+
+ explicit LSPInfo(LSPInfo&& aOther)
+ : mLsp(mozilla::Move(aOther.mLsp))
+ , mRefCnt(aOther.mRefCnt)
+ {
+ }
+
+ LSPInfo(const LSPInfo&) = delete;
+ LSPInfo& operator=(const LSPInfo&) = delete;
+
+ mozilla::UniquePtr<LSP> mLsp;
+ unsigned int mRefCnt;
+};
+
+class LSPManager
+{
+public:
+ // NB: Use default constructors and destructors
+
+ bool IncrementLSP()
+ {
+ LSPInfo& info = GetLSPInfo();
+ if (!info.mLsp) {
+ return false;
+ }
+ ++info.mRefCnt;
+ return true;
+ }
+
+ bool DecrementLSP(INT& aErrno)
+ {
+ LSPInfo& info = GetLSPInfo();
+ if (!info.mLsp) {
+ aErrno = WSANOTINITIALISED;
+ return false;
+ }
+ if (--info.mRefCnt > 0) {
+ return true;
+ }
+ MOZ_ASSERT(info.mRefCnt == 0);
+ int wsResult = info.mLsp->Cleanup(aErrno);
+ info.mLsp = nullptr;
+ return wsResult == 0;
+ }
+
+ void SetLSPInfo(mozilla::UniquePtr<LSP> aLsp)
+ {
+ MOZ_ASSERT(aLsp);
+ LSPInfo& info = GetLSPInfo();
+ info.mLsp = mozilla::Move(aLsp);
+ info.mRefCnt = 1;
+ }
+
+ void ClearLSPInfo()
+ {
+ LSPInfo& info = GetLSPInfo();
+ info.mLsp = nullptr;
+ info.mRefCnt = 0;
+ }
+
+ LSPInfo& GetLSPInfo()
+ {
+ if (!mLspInfoVec) {
+ mLspInfoVec = new mozilla::Vector<LSPInfo>();
+ }
+ int rcount = ReentryCounter::GetCount();
+ MOZ_ASSERT(rcount > 0);
+ if (size_t(rcount) > mLspInfoVec->length()) {
+ MOZ_ASSERT(size_t(rcount) == mLspInfoVec->length() + 1);
+ mLspInfoVec->resize(rcount);
+ }
+ LSPInfo& info = mLspInfoVec->begin()[rcount - 1];
+ MOZ_ASSERT((info.mLsp && info.mRefCnt > 0) ||
+ (!info.mLsp && info.mRefCnt == 0));
+ return info;
+ }
+
+private:
+ mozilla::Vector<LSPInfo>* mLspInfoVec;
+};
+
+LSPManager sLspManager;
+
+int
+LSP::TryStartup(WORD aVersionRequested,
+ LPWSPDATA aWSPData,
+ LPWSAPROTOCOL_INFOW aProtocolInfo,
+ WSPUPCALLTABLE aUpcallTable,
+ LPWSPPROC_TABLE aProcTable)
+{
+ CriticalRegion lock(sMutex);
+ ReentryCounter countme;
+ if (sLspManager.IncrementLSP()) {
+ return 0;
+ }
+ auto lsp = VerifyProtocolInfoAndLoadNextLSP(aUpcallTable, aProtocolInfo);
+ if (!lsp || !lsp->ok()) {
+ return WSASYSNOTREADY;
+ }
+ int result = lsp->Startup(aVersionRequested, aWSPData, aUpcallTable,
+ aProcTable);
+ if (!result) {
+ sLspManager.SetLSPInfo(mozilla::Move(lsp));
+ }
+ return result;
+}
+
+int WSPAPI
+LSP::WSPCleanup(LPINT aErrno)
+{
+ CriticalRegion lock(sMutex);
+ ReentryCounter countme;
+ INT error;
+ if (!sLspManager.DecrementLSP(error)) {
+ if (aErrno) {
+ *aErrno = error;
+ }
+ return SOCKET_ERROR;
+ }
+ return 0;
+}
+
+} // anonymous namespace
+
+namespace mozilla {
+namespace lsp {
+
+bool
+IsRegisteredBlockedLSP(const wchar_t* aFullyQualifiedFileName)
+{
+ ProtocolList registeredProtocols;
+
+ WSAPROTOCOL_INFOW* protocol =
+ registeredProtocols.FindLayerNamed(aFullyQualifiedFileName);
+ if (!protocol) {
+ return false;
+ }
+
+ return IsGuidBlocked(protocol->ProviderId);
+}
+
+} // namespace lsp
+} // namespace mozilla
+
+extern "C" int WSPAPI
+WSPStartup(WORD aVersionRequested,
+ LPWSPDATA aWSPData,
+ LPWSAPROTOCOL_INFOW aProtocolInfo,
+ WSPUPCALLTABLE aUpcallTable,
+ LPWSPPROC_TABLE aProcTable)
+{
+ return LSP::TryStartup(aVersionRequested, aWSPData, aProtocolInfo,
+ aUpcallTable, aProcTable);
+}
new file mode 100644
--- /dev/null
+++ b/mozglue/build/WindowsLSPPassthrough.h
@@ -0,0 +1,19 @@
+/* -*- 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_lsp_WindowsLSPPassthrough_h
+#define mozilla_lsp_WindowsLSPPassthrough_h
+
+namespace mozilla {
+namespace lsp {
+
+bool IsRegisteredBlockedLSP(const wchar_t* aFullyQualifiedFileName);
+
+} // namespace lsp
+} // namespace mozilla
+
+#endif // mozilla_lsp_WindowsLSPPassthrough_h
+
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -43,20 +43,22 @@ if not CONFIG['JS_STANDALONE']:
]
if CONFIG['OS_TARGET'] == 'WINNT':
LOCAL_INCLUDES += [
'/memory/build',
]
SOURCES += [
'WindowsDllBlocklist.cpp',
+ 'WindowsLSPPassthrough.cpp',
]
DISABLE_STL_WRAPPING = True
OS_LIBS += [
'version',
+ 'ws2_32',
]
EXPORTS.mozilla += [
'arm.h',
'mips.h',
'SSE.h',
'WindowsDllBlocklist.h',
]
--- a/mozglue/build/mozglue.def.in
+++ b/mozglue/build/mozglue.def.in
@@ -32,8 +32,9 @@ EXPORTS
_strdup=wrap_strdup
wcsdup=wrap_wcsdup
_wcsdup=wrap_wcsdup
jemalloc_stats
jemalloc_free_dirty_pages
; A hack to work around the CRT (see giant comment in Makefile.in)
frex=dumb_free_thunk
#endif
+ WSPStartup
--- a/mozglue/tests/moz.build
+++ b/mozglue/tests/moz.build
@@ -1,11 +1,19 @@
# -*- 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['OS_TARGET'] == 'WINNT':
+ DIRS += [
+ 'testdllblocklist',
+ 'testlspblocklist',
+ 'testlsp',
+ 'testlsp2',
+ ]
+
DISABLE_STL_WRAPPING = True
GeckoCppUnitTests([
'ShowSSEConfig',
], linkage=None)
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlsp/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; c-basic-offset: 4; 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/.
+
+SharedLibrary('testlsp')
+DEFFILE = SRCDIR + '/testlsp.def'
+SOURCES += [
+ 'testlsp.cpp',
+]
+OS_LIBS += [
+ 'ws2_32',
+]
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlsp/testlsp.cpp
@@ -0,0 +1,26 @@
+/* -*- 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/. */
+
+#define _WINSOCKAPI_ // Prevent windows.h from including winsock 1.0 headers
+#include <windows.h>
+#undef _WINSOCKAPI_
+#include <winsock2.h>
+#include <ws2spi.h>
+
+extern "C" int WSPAPI
+WSPStartup(WORD aVersionRequested,
+ LPWSPDATA aWSPData,
+ LPWSAPROTOCOL_INFOW aProtocolInfo,
+ WSPUPCALLTABLE aUpcallTable,
+ LPWSPPROC_TABLE aProcTable)
+{
+ return 0;
+}
+
+BOOL WINAPI
+DllMain(HINSTANCE aInstance, DWORD aReason, LPVOID aReserved)
+{
+ return TRUE;
+}
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlsp/testlsp.def
@@ -0,0 +1,8 @@
+; 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 testlsp.dll
+
+EXPORTS
+ WSPStartup
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlsp2/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; c-basic-offset: 4; 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/.
+
+SharedLibrary('testlsp2')
+DEFFILE = SRCDIR + '/testlsp2.def'
+SOURCES += [
+ '../testlsp/testlsp.cpp',
+]
+OS_LIBS += [
+ 'ws2_32',
+]
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlsp2/testlsp2.def
@@ -0,0 +1,8 @@
+; 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 testlsp2.dll
+
+EXPORTS
+ WSPStartup
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlspblocklist/TestLspBlocklist.cpp
@@ -0,0 +1,166 @@
+/* -*- 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/. */
+
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#define _WINSOCKAPI_
+#include <windows.h>
+#undef _WINSOCKAPI_
+#include <winsock2.h>
+#include <ws2spi.h>
+
+#include <stdlib.h>
+
+#include "mozilla/WindowsDLLBlocklist.h"
+
+// None of these globals are const-qualified because the WSC API declarations
+// are horrible and are missing const qualifiers all over the place :-(
+
+static wchar_t gTestLspDllName[] = L"testlsp.dll";
+static wchar_t gTestLsp2DllName[] = L"testlsp2.dll";
+static wchar_t gTestLspProvName[] = L"Mozilla DLL Blocklist Test Provider";
+static wchar_t gTestLsp2ProvName[] = L"Mozilla DLL Blocklist Test Provider 2";
+
+// The following GUIDs must match their corresponding entries in gLSPLayerGuids
+// in WindowsLSPPassthrough.cpp:
+
+// {9414BC49-7F64-4A70-98C2-BA00EC0E4360}
+static GUID gTestLspGuid =
+{ 0x9414bc49, 0x7f64, 0x4a70, { 0x98, 0xc2, 0xba, 0x0, 0xec, 0xe, 0x43, 0x60 } };
+// {6FC98C7C-48EE-41A0-A3A4-7B9E9F39B7B6}
+static GUID gTestLsp2Guid =
+{ 0x6fc98c7c, 0x48ee, 0x41a0, { 0xa3, 0xa4, 0x7b, 0x9e, 0x9f, 0x39, 0xb7, 0xb6 } };
+
+bool
+installLib(wchar_t* aLspDllName, GUID& aLayerGuid, wchar_t* aLspDesc)
+{
+ wchar_t dllPath[MAX_PATH + 1] = {0};
+ if (!_wfullpath(dllPath, aLspDllName, MAX_PATH)) {
+ printf("_wfullpath failed\n");
+ return false;
+ }
+
+ DWORD catalogId = 0;
+ INT errorCode;
+ int wsResult = WSCInstallProviderAndChains(&aLayerGuid, dllPath, aLspDesc, 0,
+ nullptr, 0, &catalogId, &errorCode);
+ if (wsResult == SOCKET_ERROR) {
+ printf("WSCInstallProvder failed for \"%S\" with code %d\n", aLspDllName,
+ errorCode);
+ return false;
+ }
+ return true;
+}
+
+bool
+deinstallLib(GUID& aLayerGuid)
+{
+ INT errorCode;
+ int wsResult = WSCDeinstallProvider(&aLayerGuid, &errorCode);
+ if (wsResult == SOCKET_ERROR) {
+ printf("WSCDeinstallProvider failed with code %d\n", errorCode);
+ return false;
+ }
+ return true;
+}
+
+bool
+deinstall()
+{
+ bool result = deinstallLib(gTestLspGuid);
+ result &= deinstallLib(gTestLsp2Guid);
+ return result;
+}
+
+bool
+install()
+{
+ int failures = 0;
+ failures += !installLib(gTestLsp2DllName, gTestLsp2Guid, gTestLsp2ProvName);
+ failures += !installLib(gTestLspDllName, gTestLspGuid, gTestLspProvName);
+ if (failures) {
+ deinstall();
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc == 2) {
+ if (strlen(argv[1]) >= 2) {
+ if (argv[1][1] == 'd') {
+ return !deinstall();
+ }
+ if (argv[1][1] == 'i') {
+ return !install();
+ }
+ }
+ printf("argument \"%s\" ignored\n", argv[1]);
+ }
+
+ if (!install()) {
+ return 1;
+ }
+
+ bool failed = false;
+ DllBlocklist_Initialize();
+ HMODULE testLoad = ::LoadLibraryW(gTestLspDllName);
+ if (!testLoad) {
+ printf("Loading the test LSP DLL unexpectedly failed with code %u\n",
+ GetLastError());
+ failed = true;
+ }
+ if (testLoad) {
+ wchar_t modulePath[MAX_PATH + 1] = {0};
+ DWORD ok = GetModuleFileNameW(testLoad, modulePath, MAX_PATH);
+ if (!ok) {
+ printf("GetModuleFileName failed with code %u\n", GetLastError());
+ failed = true;
+ }
+ wchar_t moduleFileName[_MAX_FNAME] = {0};
+ size_t moduleFileNameLen = _MAX_FNAME;
+ if (_wsplitpath_s(modulePath, nullptr, 0, nullptr, 0, moduleFileName,
+ moduleFileNameLen, nullptr, 0)) {
+ printf("_wsplitpath_s failed\n");
+ failed = true;
+ }
+ if (wcsnicmp(moduleFileName, L"mozglue", moduleFileNameLen)) {
+ printf("mozglue passthrough LSP was not substituted\n");
+ failed = true;
+ }
+ }
+
+ WSADATA wsaData;
+ int wsResult = WSAStartup(WINSOCK_VERSION, &wsaData);
+ if (wsResult) {
+ printf("WSAStartup failed with code %d\n", wsResult);
+ failed = true;
+ }
+
+ SOCKET testSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (testSocket == INVALID_SOCKET) {
+ printf("socket failed with code %d\n", WSAGetLastError());
+ failed = true;
+ }
+ if (GetModuleHandleW(gTestLspDllName)) {
+ printf("test LSP was loaded as part of protocol chain\n");
+ failed = true;
+ }
+ if (testSocket != INVALID_SOCKET) {
+ closesocket(testSocket);
+ }
+
+ WSACleanup();
+
+ failed |= !deinstall();
+ int result = 0;
+ if (failed) {
+ result = 1;
+ }
+ return result;
+}
+
new file mode 100644
--- /dev/null
+++ b/mozglue/tests/testlspblocklist/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; c-basic-offset: 4; 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 cannot currently run as part of a test harness because it requires
+# full administrator privileges.
+GeckoSimplePrograms([
+ 'TestLspBlocklist',
+], linkage=None, mozglue='program')
+
+LDFLAGS += [
+ '-MANIFEST:embed',
+ '-MANIFESTUAC:level="requireAdministrator"',
+]
+
+OS_LIBS += [
+ 'ws2_32',
+]