Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

Commit

Permalink
Bug 699134 - Protect against reentrancy in the LdrLoadDll hook, with …
Browse files Browse the repository at this point in the history
…less XPCOM stringery and more locking. a=bsmedberg ON A CLOSED TREE

--HG--
branch : GECKO80_2011110416_RELBRANCH
  • Loading branch information
Benjamin Smedberg committed Nov 18, 2011
1 parent 2d77d28 commit 601cfe6
Showing 1 changed file with 70 additions and 0 deletions.
70 changes: 70 additions & 0 deletions toolkit/xre/nsWindowsDllBlocklist.cpp
Expand Up @@ -39,6 +39,9 @@
#include <winternl.h>

#include <stdio.h>
#include <string.h>

#include <map>

#ifdef XRE_WANT_DLL_BLOCKLIST
#define XRE_SetupDllBlocklist SetupDllBlocklist
Expand All @@ -62,10 +65,68 @@
// define this for very verbose dll load debug spew
#undef DEBUG_very_verbose

namespace {

typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);

static LdrLoadDll_func stub_LdrLoadDll = 0;

/**
* Some versions of Windows call LoadLibraryEx to get the version information
* for a DLL, which causes our patched LdrLoadDll implementation to re-enter
* itself and cause infinite recursion and a stack-exhaustion crash. We protect
* against reentrancy by allowing recursive loads of the same DLL.
*
* Note that we don't use __declspec(thread) because that doesn't work in DLLs
* loaded via LoadLibrary and there can be a limited number of TLS slots, so
* we roll our own.
*/
class ReentrancySentinel
{
public:
explicit ReentrancySentinel(const char* dllName)
{
DWORD currentThreadId = GetCurrentThreadId();
EnterCriticalSection(&sLock);
mPreviousDllName = (*sThreadMap)[currentThreadId];

// If there is a DLL currently being loaded and it has the same name
// as the current attempt, we're re-entering.
mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName);
(*sThreadMap)[currentThreadId] = dllName;
LeaveCriticalSection(&sLock);
}

~ReentrancySentinel()
{
DWORD currentThreadId = GetCurrentThreadId();
EnterCriticalSection(&sLock);
(*sThreadMap)[currentThreadId] = mPreviousDllName;
LeaveCriticalSection(&sLock);
}

bool BailOut() const
{
return mReentered;
};

static void InitializeStatics()
{
InitializeCriticalSection(&sLock);
sThreadMap = new std::map<DWORD, const char*>;
}

private:
static CRITICAL_SECTION sLock;
static std::map<DWORD, const char*>* sThreadMap;

const char* mPreviousDllName;
bool mReentered;
};

CRITICAL_SECTION ReentrancySentinel::sLock;
std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;

static NTSTATUS NTAPI
patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
{
Expand Down Expand Up @@ -153,6 +214,11 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
#endif

if (info->maxVersion != ALL_VERSIONS) {
ReentrancySentinel sentinel(dllName);
if (sentinel.BailOut()) {
goto continue_loading;
}

// In Windows 8, the first parameter seems to be used for more than just the
// path name. For example, its numerical value can be 1. Passing a non-valid
// pointer to SearchPathW will cause a crash, so we need to check to see if we
Expand Down Expand Up @@ -221,11 +287,15 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam

WindowsDllInterceptor NtDllIntercept;

} // anonymous namespace

void
XRE_SetupDllBlocklist()
{
NtDllIntercept.Init("ntdll.dll");

ReentrancySentinel::InitializeStatics();

bool ok = NtDllIntercept.AddHook("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll);

#ifdef DEBUG
Expand Down

0 comments on commit 601cfe6

Please sign in to comment.