2009-11-04 09:35:20 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* 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/. */
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2016-04-12 13:37:37 +03:00
|
|
|
#ifdef MOZ_MEMORY
|
2016-03-08 01:12:31 +03:00
|
|
|
#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, ...) \
|
2017-05-16 12:46:02 +03:00
|
|
|
MOZ_MEMORY_API return_type name ## _impl(__VA_ARGS__);
|
2016-03-08 01:12:31 +03:00
|
|
|
#include "malloc_decls.h"
|
2016-04-12 13:37:37 +03:00
|
|
|
#endif
|
2016-03-08 01:12:31 +03:00
|
|
|
|
2009-11-04 09:35:20 +03:00
|
|
|
#include <windows.h>
|
|
|
|
#include <winternl.h>
|
2013-11-12 17:31:32 +04:00
|
|
|
#include <io.h>
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2013-11-12 17:31:32 +04:00
|
|
|
#pragma warning( push )
|
|
|
|
#pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h
|
2011-11-19 03:26:00 +04:00
|
|
|
#include <map>
|
2013-11-12 17:31:32 +04:00
|
|
|
#pragma warning( pop )
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2018-01-31 01:08:03 +03:00
|
|
|
#include "Authenticode.h"
|
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
This introduces the machinery needed to generate crash annotations from a YAML
file. The relevant C++ functions are updated to take a typed enum. JavaScript
calls are unaffected but they will throw if the string argument does not
correspond to one of the known entries in the C++ enum. The existing whitelists
and blacklists of annotations are also generated from the YAML file and all
duplicate code related to them has been consolidated. Once written out to the
.extra file the annotations are converted in string form and are no different
than the existing ones.
All existing annotations have been included in the list (and some obsolete ones
have been removed) and all call sites have been updated including tests where
appropriate.
--HG--
extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
2018-07-05 16:42:11 +03:00
|
|
|
#include "CrashAnnotations.h"
|
2009-11-04 09:35:20 +03:00
|
|
|
#include "nsAutoPtr.h"
|
|
|
|
#include "nsWindowsDllInterceptor.h"
|
2016-12-09 04:47:19 +03:00
|
|
|
#include "mozilla/Sprintf.h"
|
2017-04-20 22:09:11 +03:00
|
|
|
#include "mozilla/StackWalk_windows.h"
|
2015-12-25 05:38:44 +03:00
|
|
|
#include "mozilla/UniquePtr.h"
|
2018-02-22 15:41:15 +03:00
|
|
|
#include "mozilla/Vector.h"
|
2013-10-18 22:14:31 +04:00
|
|
|
#include "mozilla/WindowsVersion.h"
|
2013-10-18 22:24:51 +04:00
|
|
|
#include "nsWindowsHelpers.h"
|
2016-05-25 00:57:57 +03:00
|
|
|
#include "WindowsDllBlocklist.h"
|
2017-06-07 05:37:18 +03:00
|
|
|
#include "mozilla/AutoProfilerLabel.h"
|
2018-01-31 00:23:10 +03:00
|
|
|
#include "mozilla/glue/WindowsDllServices.h"
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2012-04-10 23:52:56 +04:00
|
|
|
using namespace mozilla;
|
|
|
|
|
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
This introduces the machinery needed to generate crash annotations from a YAML
file. The relevant C++ functions are updated to take a typed enum. JavaScript
calls are unaffected but they will throw if the string argument does not
correspond to one of the known entries in the C++ enum. The existing whitelists
and blacklists of annotations are also generated from the YAML file and all
duplicate code related to them has been consolidated. Once written out to the
.extra file the annotations are converted in string form and are no different
than the existing ones.
All existing annotations have been included in the list (and some obsolete ones
have been removed) and all call sites have been updated including tests where
appropriate.
--HG--
extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
2018-07-05 16:42:11 +03:00
|
|
|
using CrashReporter::Annotation;
|
|
|
|
using CrashReporter::AnnotationToString;
|
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
#define DLL_BLOCKLIST_ENTRY(name, ...) \
|
|
|
|
{ name, __VA_ARGS__ },
|
2018-07-07 03:11:48 +03:00
|
|
|
#define DLL_BLOCKLIST_STRING_TYPE const char*
|
2018-06-06 00:16:13 +03:00
|
|
|
#include "mozilla/WindowsDllBlocklistDefs.h"
|
2009-11-05 23:23:06 +03:00
|
|
|
|
2009-11-11 01:34:41 +03:00
|
|
|
// define this for very verbose dll load debug spew
|
|
|
|
#undef DEBUG_very_verbose
|
|
|
|
|
2017-05-12 00:50:10 +03:00
|
|
|
static uint32_t sInitFlags;
|
2016-07-08 02:00:23 +03:00
|
|
|
static bool sBlocklistInitAttempted;
|
2013-11-12 17:31:32 +04:00
|
|
|
static bool sBlocklistInitFailed;
|
|
|
|
static bool sUser32BeforeBlocklist;
|
|
|
|
|
|
|
|
// Duplicated from xpcom glue. Ideally this should be shared.
|
2013-12-17 15:01:39 +04:00
|
|
|
void
|
2013-11-12 17:31:32 +04:00
|
|
|
printf_stderr(const char *fmt, ...)
|
|
|
|
{
|
|
|
|
if (IsDebuggerPresent()) {
|
|
|
|
char buf[2048];
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
2016-12-09 04:47:19 +03:00
|
|
|
VsprintfLiteral(buf, fmt, args);
|
2013-11-12 17:31:32 +04:00
|
|
|
va_end(args);
|
|
|
|
OutputDebugStringA(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *fp = _fdopen(_dup(2), "a");
|
|
|
|
if (!fp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
vfprintf(fp, fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
}
|
2012-02-23 03:05:28 +04:00
|
|
|
|
2017-05-24 12:56:40 +03:00
|
|
|
|
2017-08-25 06:49:09 +03:00
|
|
|
typedef MOZ_NORETURN_PTR void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
|
2018-06-27 20:51:10 +03:00
|
|
|
static WindowsDllInterceptor::FuncHookType<BaseThreadInitThunk_func> stub_BaseThreadInitThunk;
|
2017-05-24 12:56:40 +03:00
|
|
|
|
2009-11-04 09:35:20 +03:00
|
|
|
typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
|
2018-06-27 20:51:10 +03:00
|
|
|
static WindowsDllInterceptor::FuncHookType<LdrLoadDll_func> stub_LdrLoadDll;
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2017-05-18 23:15:29 +03:00
|
|
|
#ifdef _M_AMD64
|
|
|
|
typedef decltype(RtlInstallFunctionTableCallback)* RtlInstallFunctionTableCallback_func;
|
2018-06-27 20:51:10 +03:00
|
|
|
static WindowsDllInterceptor::FuncHookType<RtlInstallFunctionTableCallback_func> stub_RtlInstallFunctionTableCallback;
|
2017-05-18 23:15:29 +03:00
|
|
|
|
|
|
|
extern uint8_t* sMsMpegJitCodeRegionStart;
|
|
|
|
extern size_t sMsMpegJitCodeRegionSize;
|
|
|
|
|
|
|
|
BOOLEAN WINAPI patched_RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,
|
|
|
|
DWORD64 BaseAddress, DWORD Length, PGET_RUNTIME_FUNCTION_CALLBACK Callback,
|
|
|
|
PVOID Context, PCWSTR OutOfProcessCallbackDll)
|
|
|
|
{
|
|
|
|
// msmpeg2vdec.dll sets up a function table callback for their JIT code that
|
|
|
|
// just terminates the process, because their JIT doesn't have unwind info.
|
|
|
|
// If we see this callback being registered, record the region address, so
|
|
|
|
// that StackWalk.cpp can avoid unwinding addresses in this region.
|
|
|
|
//
|
|
|
|
// To keep things simple I'm not tracking unloads of msmpeg2vdec.dll.
|
|
|
|
// Worst case the stack walker will needlessly avoid a few pages of memory.
|
|
|
|
|
|
|
|
// Tricky: GetModuleHandleExW adds a ref by default; GetModuleHandleW doesn't.
|
|
|
|
HMODULE callbackModule = nullptr;
|
|
|
|
DWORD moduleFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|
|
|
|
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
|
|
|
|
|
|
|
|
// These GetModuleHandle calls enter a critical section on Win7.
|
|
|
|
AutoSuppressStackWalking suppress;
|
|
|
|
|
|
|
|
if (GetModuleHandleExW(moduleFlags, (LPWSTR)Callback, &callbackModule) &&
|
|
|
|
GetModuleHandleW(L"msmpeg2vdec.dll") == callbackModule) {
|
|
|
|
sMsMpegJitCodeRegionStart = (uint8_t*)BaseAddress;
|
|
|
|
sMsMpegJitCodeRegionSize = Length;
|
|
|
|
}
|
|
|
|
|
|
|
|
return stub_RtlInstallFunctionTableCallback(TableIdentifier, BaseAddress,
|
|
|
|
Length, Callback, Context,
|
|
|
|
OutOfProcessCallbackDll);
|
|
|
|
}
|
|
|
|
#endif
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2012-02-23 03:05:28 +04:00
|
|
|
template <class T>
|
|
|
|
struct RVAMap {
|
|
|
|
RVAMap(HANDLE map, DWORD offset) {
|
|
|
|
SYSTEM_INFO info;
|
|
|
|
GetSystemInfo(&info);
|
|
|
|
|
|
|
|
DWORD alignedOffset = (offset / info.dwAllocationGranularity) *
|
|
|
|
info.dwAllocationGranularity;
|
|
|
|
|
2013-11-12 17:31:32 +04:00
|
|
|
MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf");
|
2012-02-23 03:05:28 +04:00
|
|
|
|
|
|
|
mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset,
|
|
|
|
sizeof(T) + (offset - alignedOffset));
|
|
|
|
|
2012-04-29 01:11:40 +04:00
|
|
|
mMappedView = mRealView ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset)) :
|
2012-07-30 18:20:58 +04:00
|
|
|
nullptr;
|
2012-02-23 03:05:28 +04:00
|
|
|
}
|
|
|
|
~RVAMap() {
|
|
|
|
if (mRealView) {
|
|
|
|
::UnmapViewOfFile(mRealView);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
operator const T*() const { return mMappedView; }
|
|
|
|
const T* operator->() const { return mMappedView; }
|
|
|
|
private:
|
|
|
|
const T* mMappedView;
|
|
|
|
void* mRealView;
|
|
|
|
};
|
|
|
|
|
2017-05-18 23:05:32 +03:00
|
|
|
static DWORD
|
2014-07-01 01:33:43 +04:00
|
|
|
GetTimestamp(const wchar_t* path)
|
|
|
|
{
|
|
|
|
DWORD timestamp = 0;
|
|
|
|
|
|
|
|
HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
|
|
|
|
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
|
|
|
nullptr);
|
|
|
|
if (file != INVALID_HANDLE_VALUE) {
|
|
|
|
HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0,
|
|
|
|
nullptr);
|
|
|
|
if (map) {
|
|
|
|
RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
|
|
|
|
if (peHeader) {
|
|
|
|
RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
|
|
|
|
if (ntHeader) {
|
|
|
|
timestamp = ntHeader->FileHeader.TimeDateStamp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
::CloseHandle(map);
|
|
|
|
}
|
|
|
|
::CloseHandle(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
return timestamp;
|
|
|
|
}
|
|
|
|
|
2013-10-18 22:24:50 +04:00
|
|
|
// This lock protects both the reentrancy sentinel and the crash reporter
|
|
|
|
// data structures.
|
|
|
|
static CRITICAL_SECTION sLock;
|
|
|
|
|
2011-11-19 03:26:00 +04:00
|
|
|
/**
|
|
|
|
* 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();
|
2013-10-18 22:24:51 +04:00
|
|
|
AutoCriticalSection lock(&sLock);
|
2011-11-19 03:26:00 +04:00
|
|
|
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;
|
|
|
|
}
|
2016-09-23 05:18:04 +03:00
|
|
|
|
2011-11-19 03:26:00 +04:00
|
|
|
~ReentrancySentinel()
|
|
|
|
{
|
|
|
|
DWORD currentThreadId = GetCurrentThreadId();
|
2013-10-18 22:24:51 +04:00
|
|
|
AutoCriticalSection lock(&sLock);
|
2011-11-19 03:26:00 +04:00
|
|
|
(*sThreadMap)[currentThreadId] = mPreviousDllName;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BailOut() const
|
|
|
|
{
|
|
|
|
return mReentered;
|
|
|
|
};
|
2016-09-23 05:18:04 +03:00
|
|
|
|
2011-11-19 03:26:00 +04:00
|
|
|
static void InitializeStatics()
|
|
|
|
{
|
|
|
|
InitializeCriticalSection(&sLock);
|
|
|
|
sThreadMap = new std::map<DWORD, const char*>;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static std::map<DWORD, const char*>* sThreadMap;
|
|
|
|
|
|
|
|
const char* mPreviousDllName;
|
|
|
|
bool mReentered;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
|
|
|
|
|
2013-10-18 22:24:50 +04:00
|
|
|
/**
|
|
|
|
* This is a linked list of DLLs that have been blocked. It doesn't use
|
|
|
|
* mozilla::LinkedList because this is an append-only list and doesn't need
|
|
|
|
* to be doubly linked.
|
|
|
|
*/
|
|
|
|
class DllBlockSet
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static void Add(const char* name, unsigned long long version);
|
|
|
|
|
|
|
|
// Write the list of blocked DLLs to a file HANDLE. This method is run after
|
|
|
|
// a crash occurs and must therefore not use the heap, etc.
|
|
|
|
static void Write(HANDLE file);
|
|
|
|
|
|
|
|
private:
|
|
|
|
DllBlockSet(const char* name, unsigned long long version)
|
|
|
|
: mName(name)
|
|
|
|
, mVersion(version)
|
|
|
|
, mNext(nullptr)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
const char* mName; // points into the gWindowsDllBlocklist string
|
2013-10-18 22:24:50 +04:00
|
|
|
unsigned long long mVersion;
|
|
|
|
DllBlockSet* mNext;
|
|
|
|
|
|
|
|
static DllBlockSet* gFirst;
|
|
|
|
};
|
|
|
|
|
|
|
|
DllBlockSet* DllBlockSet::gFirst;
|
|
|
|
|
|
|
|
void
|
|
|
|
DllBlockSet::Add(const char* name, unsigned long long version)
|
|
|
|
{
|
2013-10-18 22:24:51 +04:00
|
|
|
AutoCriticalSection lock(&sLock);
|
2013-10-18 22:24:50 +04:00
|
|
|
for (DllBlockSet* b = gFirst; b; b = b->mNext) {
|
|
|
|
if (0 == strcmp(b->mName, name) && b->mVersion == version) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Not already present
|
|
|
|
DllBlockSet* n = new DllBlockSet(name, version);
|
|
|
|
n->mNext = gFirst;
|
|
|
|
gFirst = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DllBlockSet::Write(HANDLE file)
|
|
|
|
{
|
2015-09-11 02:52:30 +03:00
|
|
|
// It would be nicer to use AutoCriticalSection here. However, its destructor
|
|
|
|
// might not run if an exception occurs, in which case we would never leave
|
|
|
|
// the critical section. (MSVC warns about this possibility.) So we
|
|
|
|
// enter and leave manually.
|
|
|
|
::EnterCriticalSection(&sLock);
|
2013-10-18 22:24:50 +04:00
|
|
|
|
|
|
|
// Because this method is called after a crash occurs, and uses heap memory,
|
|
|
|
// protect this entire block with a structured exception handler.
|
2013-12-17 15:01:39 +04:00
|
|
|
MOZ_SEH_TRY {
|
2015-09-11 02:52:30 +03:00
|
|
|
DWORD nBytes;
|
2013-10-18 22:24:50 +04:00
|
|
|
for (DllBlockSet* b = gFirst; b; b = b->mNext) {
|
|
|
|
// write name[,v.v.v.v];
|
|
|
|
WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
|
2017-08-30 08:43:46 +03:00
|
|
|
if (b->mVersion != ALL_VERSIONS) {
|
2013-10-18 22:24:50 +04:00
|
|
|
WriteFile(file, ",", 1, &nBytes, nullptr);
|
|
|
|
uint16_t parts[4];
|
|
|
|
parts[0] = b->mVersion >> 48;
|
|
|
|
parts[1] = (b->mVersion >> 32) & 0xFFFF;
|
|
|
|
parts[2] = (b->mVersion >> 16) & 0xFFFF;
|
|
|
|
parts[3] = b->mVersion & 0xFFFF;
|
|
|
|
for (int p = 0; p < 4; ++p) {
|
|
|
|
char buf[32];
|
|
|
|
ltoa(parts[p], buf, 10);
|
|
|
|
WriteFile(file, buf, strlen(buf), &nBytes, nullptr);
|
|
|
|
if (p != 3) {
|
|
|
|
WriteFile(file, ".", 1, &nBytes, nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WriteFile(file, ";", 1, &nBytes, nullptr);
|
|
|
|
}
|
|
|
|
}
|
2013-12-17 15:01:39 +04:00
|
|
|
MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { }
|
2015-09-11 02:52:30 +03:00
|
|
|
|
|
|
|
::LeaveCriticalSection(&sLock);
|
2013-10-18 22:24:50 +04:00
|
|
|
}
|
|
|
|
|
2015-12-25 05:38:44 +03:00
|
|
|
static UniquePtr<wchar_t[]>
|
|
|
|
getFullPath (PWCHAR filePath, wchar_t* fname)
|
2009-11-04 09:35:20 +03:00
|
|
|
{
|
2012-02-23 03:05:28 +04:00
|
|
|
// 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
|
2013-10-11 00:36:42 +04:00
|
|
|
// are handed a valid pointer, and otherwise just pass nullptr to SearchPathW.
|
2016-02-18 02:56:00 +03:00
|
|
|
PWCHAR sanitizedFilePath = nullptr;
|
|
|
|
if ((uintptr_t(filePath) >= 65536) && ((uintptr_t(filePath) & 1) == 0)) {
|
|
|
|
sanitizedFilePath = filePath;
|
|
|
|
}
|
2012-02-23 03:05:28 +04:00
|
|
|
|
|
|
|
// figure out the length of the string that we need
|
2013-10-11 00:36:42 +04:00
|
|
|
DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr,
|
|
|
|
nullptr);
|
2012-02-23 03:05:28 +04:00
|
|
|
if (pathlen == 0) {
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-02-23 03:05:28 +04:00
|
|
|
}
|
|
|
|
|
2015-12-25 05:38:44 +03:00
|
|
|
auto full_fname = MakeUnique<wchar_t[]>(pathlen+1);
|
2012-02-23 03:05:28 +04:00
|
|
|
if (!full_fname) {
|
|
|
|
// couldn't allocate memory?
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2012-02-23 03:05:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// now actually grab it
|
2015-12-25 05:38:44 +03:00
|
|
|
SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname.get(),
|
2013-10-11 00:36:42 +04:00
|
|
|
nullptr);
|
2012-03-01 23:07:11 +04:00
|
|
|
return full_fname;
|
|
|
|
}
|
|
|
|
|
2013-10-18 22:24:50 +04:00
|
|
|
// No builtin function to find the last character matching a set
|
|
|
|
static wchar_t* lastslash(wchar_t* s, int len)
|
|
|
|
{
|
|
|
|
for (wchar_t* c = s + len - 1; c >= s; --c) {
|
|
|
|
if (*c == L'\\' || *c == L'/') {
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-14 10:15:26 +03:00
|
|
|
|
|
|
|
#ifdef ENABLE_TESTS
|
|
|
|
DllLoadHookType gDllLoadHook = nullptr;
|
|
|
|
|
|
|
|
void
|
|
|
|
DllBlocklist_SetDllLoadHook(DllLoadHookType aHook)
|
|
|
|
{
|
|
|
|
gDllLoadHook = aHook;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CallDllLoadHook(bool aDllLoaded, NTSTATUS aStatus, HANDLE aDllBase, PUNICODE_STRING aDllName)
|
|
|
|
{
|
|
|
|
if (gDllLoadHook) {
|
|
|
|
gDllLoadHook(aDllLoaded, aStatus, aDllBase, aDllName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CreateThreadHookType gCreateThreadHook = nullptr;
|
|
|
|
|
|
|
|
void
|
|
|
|
DllBlocklist_SetCreateThreadHook(CreateThreadHookType aHook)
|
|
|
|
{
|
|
|
|
gCreateThreadHook = aHook;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
CallCreateThreadHook(bool aWasAllowed, void* aStartAddress)
|
|
|
|
{
|
|
|
|
if (gCreateThreadHook) {
|
|
|
|
gCreateThreadHook(aWasAllowed, aStartAddress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-11 01:01:10 +03:00
|
|
|
// This function does the work for gtest TestDllBlocklist.BlocklistIntegrity.
|
|
|
|
// If the test fails, the return value is the pointer to a string description
|
|
|
|
// of the failure. If the test passes, the return value is nullptr.
|
|
|
|
const char*
|
|
|
|
DllBlocklist_TestBlocklistIntegrity()
|
|
|
|
{
|
|
|
|
mozilla::Vector<DLL_BLOCKLIST_STRING_TYPE> dupes;
|
|
|
|
DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(pFirst);
|
|
|
|
DECLARE_POINTER_TO_LAST_DLL_BLOCKLIST_ENTRY(pLast);
|
|
|
|
|
|
|
|
if (pLast->name || pLast->maxVersion || pLast->flags) {
|
|
|
|
return "The last dll blocklist entry must be all-null.";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0; i < mozilla::ArrayLength(gWindowsDllBlocklist) - 1; ++i) {
|
|
|
|
auto pEntry = pFirst + i;
|
|
|
|
|
|
|
|
// Validate name
|
|
|
|
if (!pEntry->name) {
|
|
|
|
return "A dll blocklist entry contains a null name.";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(pEntry->name) <= 2) {
|
|
|
|
return "Dll blocklist entry names must be longer than 2 characters.";
|
|
|
|
}
|
|
|
|
// Check the filename for valid characters.
|
|
|
|
for (auto pch = pEntry->name; *pch != 0; ++pch) {
|
|
|
|
if (*pch >= 'A' && *pch <= 'Z') {
|
|
|
|
return "Dll blocklist entry names cannot contain uppercase characters.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for duplicate entries
|
|
|
|
for (auto dupe : dupes) {
|
|
|
|
if (!stricmp(dupe, pEntry->name)) {
|
|
|
|
return "At least one duplicate dll blocklist entry was found.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!dupes.append(pEntry->name)) {
|
|
|
|
return "Failed to append to duplicates list; test unable to continue.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-14 10:15:26 +03:00
|
|
|
#else // ENABLE_TESTS
|
|
|
|
#define CallDllLoadHook(...)
|
|
|
|
#define CallCreateThreadHook(...)
|
|
|
|
#endif // ENABLE_TESTS
|
|
|
|
|
2012-03-01 23:07:11 +04:00
|
|
|
static NTSTATUS NTAPI
|
|
|
|
patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
|
|
|
|
{
|
|
|
|
// We have UCS2 (UTF16?), we want ASCII, but we also just want the filename portion
|
|
|
|
#define DLLNAME_MAX 128
|
|
|
|
char dllName[DLLNAME_MAX+1];
|
|
|
|
wchar_t *dll_part;
|
2014-05-12 13:01:53 +04:00
|
|
|
char *dot;
|
2012-03-01 23:07:11 +04:00
|
|
|
|
|
|
|
int len = moduleFileName->Length / 2;
|
|
|
|
wchar_t *fname = moduleFileName->Buffer;
|
2015-12-25 05:38:44 +03:00
|
|
|
UniquePtr<wchar_t[]> full_fname;
|
2012-02-23 03:05:28 +04:00
|
|
|
|
2009-11-11 01:34:41 +03:00
|
|
|
// The filename isn't guaranteed to be null terminated, but in practice
|
|
|
|
// it always will be; ensure that this is so, and bail if not.
|
|
|
|
// This is done instead of the more robust approach because of bug 527122,
|
|
|
|
// where lots of weird things were happening when we tried to make a copy.
|
|
|
|
if (moduleFileName->MaximumLength < moduleFileName->Length+2 ||
|
|
|
|
fname[len] != 0)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf_stderr("LdrLoadDll: non-null terminated string found!\n");
|
|
|
|
#endif
|
|
|
|
goto continue_loading;
|
|
|
|
}
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2013-10-18 22:24:50 +04:00
|
|
|
dll_part = lastslash(fname, len);
|
2009-11-04 09:35:20 +03:00
|
|
|
if (dll_part) {
|
|
|
|
dll_part = dll_part + 1;
|
2009-11-11 01:34:41 +03:00
|
|
|
len -= dll_part - fname;
|
2009-11-04 09:35:20 +03:00
|
|
|
} else {
|
|
|
|
dll_part = fname;
|
|
|
|
}
|
|
|
|
|
2009-11-11 01:34:41 +03:00
|
|
|
#ifdef DEBUG_very_verbose
|
|
|
|
printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len);
|
|
|
|
#endif
|
|
|
|
|
2009-11-04 09:35:20 +03:00
|
|
|
// if it's too long, then, we assume we won't want to block it,
|
|
|
|
// since DLLNAME_MAX should be at least long enough to hold the longest
|
|
|
|
// entry in our blocklist.
|
2009-11-11 01:34:41 +03:00
|
|
|
if (len > DLLNAME_MAX) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf_stderr("LdrLoadDll: len too long! %d\n", len);
|
|
|
|
#endif
|
2009-11-04 09:35:20 +03:00
|
|
|
goto continue_loading;
|
2009-11-11 01:34:41 +03:00
|
|
|
}
|
2009-11-04 09:35:20 +03:00
|
|
|
|
|
|
|
// copy over to our char byte buffer, lowercasing ASCII as we go
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
|
|
wchar_t c = dll_part[i];
|
|
|
|
|
|
|
|
if (c > 0x7f) {
|
|
|
|
// welp, it's not ascii; if we need to add non-ascii things to
|
|
|
|
// our blocklist, we'll have to remove this limitation.
|
|
|
|
goto continue_loading;
|
|
|
|
}
|
|
|
|
|
2009-11-11 01:34:41 +03:00
|
|
|
// ensure that dll name is all lowercase
|
|
|
|
if (c >= 'A' && c <= 'Z')
|
|
|
|
c += 'a' - 'A';
|
|
|
|
|
2009-11-04 09:35:20 +03:00
|
|
|
dllName[i] = (char) c;
|
|
|
|
}
|
|
|
|
|
|
|
|
dllName[len] = 0;
|
|
|
|
|
2009-11-11 01:34:41 +03:00
|
|
|
#ifdef DEBUG_very_verbose
|
|
|
|
printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
|
|
|
|
#endif
|
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
if (!(sInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
|
|
|
|
// Block a suspicious binary that uses various 12-digit hex strings
|
|
|
|
// e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
|
|
|
|
dot = strchr(dllName, '.');
|
|
|
|
if (dot && (strchr(dot+1, '.') == dot+13)) {
|
|
|
|
char * end = nullptr;
|
|
|
|
_strtoui64(dot+1, &end, 16);
|
|
|
|
if (end == dot+13) {
|
2018-06-14 10:15:26 +03:00
|
|
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
2018-06-06 00:16:13 +03:00
|
|
|
return STATUS_DLL_NOT_FOUND;
|
|
|
|
}
|
2018-06-07 12:09:22 +03:00
|
|
|
}
|
2018-06-06 00:16:13 +03:00
|
|
|
// Block binaries where the filename is at least 16 hex digits
|
|
|
|
if (dot && ((dot - dllName) >= 16)) {
|
|
|
|
char * current = dllName;
|
|
|
|
while (current < dot && isxdigit(*current)) {
|
|
|
|
current++;
|
|
|
|
}
|
|
|
|
if (current == dot) {
|
2018-06-14 10:15:26 +03:00
|
|
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
2018-06-06 00:16:13 +03:00
|
|
|
return STATUS_DLL_NOT_FOUND;
|
|
|
|
}
|
2015-04-10 22:23:32 +03:00
|
|
|
}
|
2014-04-15 03:40:42 +04:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
// then compare to everything on the blocklist
|
|
|
|
DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info);
|
|
|
|
while (info->name) {
|
|
|
|
if (strcmp(info->name, dllName) == 0)
|
|
|
|
break;
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
info++;
|
|
|
|
}
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
if (info->name) {
|
|
|
|
bool load_ok = false;
|
2009-11-11 01:34:41 +03:00
|
|
|
|
|
|
|
#ifdef DEBUG_very_verbose
|
2018-06-06 00:16:13 +03:00
|
|
|
printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
|
2009-11-11 01:34:41 +03:00
|
|
|
#endif
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
if ((info->flags & DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
|
|
|
|
!IsWin8OrLater()) {
|
|
|
|
goto continue_loading;
|
|
|
|
}
|
2018-06-07 12:09:22 +03:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
if ((info->flags & DllBlockInfo::BLOCK_WIN8_ONLY) &&
|
|
|
|
(!IsWin8OrLater() || IsWin8Point1OrLater())) {
|
2018-06-06 00:16:13 +03:00
|
|
|
goto continue_loading;
|
2012-03-01 23:07:11 +04:00
|
|
|
}
|
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
if ((info->flags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
|
|
|
|
!(sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
|
|
|
|
goto continue_loading;
|
2018-06-07 12:09:22 +03:00
|
|
|
}
|
2018-06-06 00:16:13 +03:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
unsigned long long fVersion = ALL_VERSIONS;
|
|
|
|
|
|
|
|
if (info->maxVersion != ALL_VERSIONS) {
|
|
|
|
ReentrancySentinel sentinel(dllName);
|
|
|
|
if (sentinel.BailOut()) {
|
|
|
|
goto continue_loading;
|
2018-06-06 00:16:13 +03:00
|
|
|
}
|
2018-06-06 00:16:13 +03:00
|
|
|
|
|
|
|
full_fname = getFullPath(filePath, fname);
|
|
|
|
if (!full_fname) {
|
|
|
|
// uh, we couldn't find the DLL at all, so...
|
|
|
|
printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
|
2018-06-14 10:15:26 +03:00
|
|
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
2018-06-06 00:16:13 +03:00
|
|
|
return STATUS_DLL_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->flags & DllBlockInfo::USE_TIMESTAMP) {
|
|
|
|
fVersion = GetTimestamp(full_fname.get());
|
|
|
|
if (fVersion > info->maxVersion) {
|
|
|
|
load_ok = true;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DWORD zero;
|
|
|
|
DWORD infoSize = GetFileVersionInfoSizeW(full_fname.get(), &zero);
|
|
|
|
|
|
|
|
// If we failed to get the version information, we block.
|
|
|
|
|
|
|
|
if (infoSize != 0) {
|
|
|
|
auto infoData = MakeUnique<unsigned char[]>(infoSize);
|
|
|
|
VS_FIXEDFILEINFO *vInfo;
|
|
|
|
UINT vInfoLen;
|
|
|
|
|
|
|
|
if (GetFileVersionInfoW(full_fname.get(), 0, infoSize, infoData.get()) &&
|
|
|
|
VerQueryValueW(infoData.get(), L"\\", (LPVOID*) &vInfo, &vInfoLen))
|
|
|
|
{
|
|
|
|
fVersion =
|
|
|
|
((unsigned long long)vInfo->dwFileVersionMS) << 32 |
|
|
|
|
((unsigned long long)vInfo->dwFileVersionLS);
|
|
|
|
|
|
|
|
// finally do the version check, and if it's greater than our block
|
|
|
|
// version, keep loading
|
|
|
|
if (fVersion > info->maxVersion)
|
|
|
|
load_ok = true;
|
|
|
|
}
|
2014-07-01 01:33:43 +04:00
|
|
|
}
|
2009-11-04 09:35:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
if (!load_ok) {
|
|
|
|
printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
|
|
|
|
DllBlockSet::Add(info->name, fVersion);
|
2018-06-14 10:15:26 +03:00
|
|
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
2018-06-06 00:16:13 +03:00
|
|
|
return STATUS_DLL_NOT_FOUND;
|
|
|
|
}
|
2009-11-11 01:34:41 +03:00
|
|
|
}
|
2009-11-04 09:35:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
continue_loading:
|
2009-11-11 01:34:41 +03:00
|
|
|
#ifdef DEBUG_very_verbose
|
2018-06-06 00:16:13 +03:00
|
|
|
printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
|
2009-11-11 01:34:41 +03:00
|
|
|
#endif
|
|
|
|
|
2017-06-07 05:37:18 +03:00
|
|
|
// A few DLLs such as xul.dll and nss3.dll get loaded before mozglue's
|
|
|
|
// AutoProfilerLabel is initialized, and this is a no-op in those cases. But
|
|
|
|
// the vast majority of DLLs do get labelled here.
|
|
|
|
AutoProfilerLabel label("WindowsDllBlocklist::patched_LdrLoadDll", dllName,
|
|
|
|
__LINE__);
|
|
|
|
|
2017-05-03 19:10:48 +03:00
|
|
|
#ifdef _M_AMD64
|
2017-04-20 22:09:11 +03:00
|
|
|
// Prevent the stack walker from suspending this thread when LdrLoadDll
|
|
|
|
// holds the RtlLookupFunctionEntry lock.
|
2017-05-03 19:10:48 +03:00
|
|
|
AutoSuppressStackWalking suppress;
|
|
|
|
#endif
|
2017-04-20 22:09:11 +03:00
|
|
|
|
2018-06-14 10:15:26 +03:00
|
|
|
NTSTATUS ret = stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
|
2018-09-21 00:58:04 +03:00
|
|
|
CallDllLoadHook(NT_SUCCESS(ret), ret, handle ? *handle : 0, moduleFileName);
|
2018-06-14 10:15:26 +03:00
|
|
|
return ret;
|
2009-11-04 09:35:20 +03:00
|
|
|
}
|
|
|
|
|
2018-02-22 15:41:15 +03:00
|
|
|
#if defined(NIGHTLY_BUILD)
|
|
|
|
// Map of specific thread proc addresses we should block. In particular,
|
|
|
|
// LoadLibrary* APIs which indicate DLL injection
|
|
|
|
static mozilla::Vector<void*, 4>* gStartAddressesToBlock;
|
|
|
|
#endif
|
|
|
|
|
2017-05-24 12:56:40 +03:00
|
|
|
static bool
|
|
|
|
ShouldBlockThread(void* aStartAddress)
|
|
|
|
{
|
|
|
|
// Allows crashfirefox.exe to continue to work. Also if your threadproc is null, this crash is intentional.
|
|
|
|
if (aStartAddress == 0)
|
|
|
|
return false;
|
|
|
|
|
2018-02-22 15:41:15 +03:00
|
|
|
#if defined(NIGHTLY_BUILD)
|
|
|
|
for (auto p : *gStartAddressesToBlock) {
|
|
|
|
if (p == aStartAddress) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-05-24 12:56:40 +03:00
|
|
|
bool shouldBlock = false;
|
|
|
|
MEMORY_BASIC_INFORMATION startAddressInfo = {0};
|
|
|
|
if (VirtualQuery(aStartAddress, &startAddressInfo, sizeof(startAddressInfo))) {
|
|
|
|
shouldBlock |= startAddressInfo.State != MEM_COMMIT;
|
|
|
|
shouldBlock |= startAddressInfo.Protect != PAGE_EXECUTE_READ;
|
|
|
|
}
|
|
|
|
|
|
|
|
return shouldBlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allows blocked threads to still run normally through BaseThreadInitThunk, in case there's any magic there that we shouldn't skip.
|
|
|
|
static DWORD WINAPI
|
|
|
|
NopThreadProc(void* /* aThreadParam */)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MOZ_NORETURN void __fastcall
|
|
|
|
patched_BaseThreadInitThunk(BOOL aIsInitialThread, void* aStartAddress,
|
|
|
|
void* aThreadParam)
|
|
|
|
{
|
|
|
|
if (ShouldBlockThread(aStartAddress)) {
|
2018-06-14 10:15:26 +03:00
|
|
|
CallCreateThreadHook(false, aStartAddress);
|
2017-07-11 22:26:35 +03:00
|
|
|
aStartAddress = (void*)NopThreadProc;
|
2018-06-14 10:15:26 +03:00
|
|
|
} else {
|
|
|
|
CallCreateThreadHook(true, aStartAddress);
|
2017-05-24 12:56:40 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-18 23:05:32 +03:00
|
|
|
static WindowsDllInterceptor NtDllIntercept;
|
2017-05-18 23:15:29 +03:00
|
|
|
static WindowsDllInterceptor Kernel32Intercept;
|
2011-11-19 03:26:00 +04:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
static void
|
|
|
|
GetNativeNtBlockSetWriter();
|
|
|
|
|
2016-05-25 00:57:57 +03:00
|
|
|
MFBT_API void
|
2017-05-12 00:50:10 +03:00
|
|
|
DllBlocklist_Initialize(uint32_t aInitFlags)
|
2009-11-04 09:35:20 +03:00
|
|
|
{
|
2016-07-08 02:00:23 +03:00
|
|
|
if (sBlocklistInitAttempted) {
|
|
|
|
return;
|
|
|
|
}
|
2017-05-12 00:50:10 +03:00
|
|
|
sInitFlags = aInitFlags;
|
2018-06-06 00:16:13 +03:00
|
|
|
|
|
|
|
if (sInitFlags & eDllBlocklistInitFlagWasBootstrapped) {
|
|
|
|
GetNativeNtBlockSetWriter();
|
|
|
|
}
|
|
|
|
|
2016-07-08 02:00:23 +03:00
|
|
|
sBlocklistInitAttempted = true;
|
2018-03-17 23:14:18 +03:00
|
|
|
#if defined(NIGHTLY_BUILD)
|
2018-02-22 15:41:15 +03:00
|
|
|
gStartAddressesToBlock = new mozilla::Vector<void*, 4>;
|
2018-03-17 23:14:18 +03:00
|
|
|
#endif
|
2016-07-08 02:00:23 +03:00
|
|
|
|
2017-01-12 05:13:40 +03:00
|
|
|
// In order to be effective against AppInit DLLs, the blocklist must be
|
|
|
|
// initialized before user32.dll is loaded into the process (bug 932100).
|
2013-11-12 17:31:32 +04:00
|
|
|
if (GetModuleHandleA("user32.dll")) {
|
|
|
|
sUser32BeforeBlocklist = true;
|
2017-01-12 05:13:40 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf_stderr("DLL blocklist was unable to intercept AppInit DLLs.\n");
|
|
|
|
#endif
|
2013-11-12 17:31:32 +04:00
|
|
|
}
|
|
|
|
|
2009-11-04 09:35:20 +03:00
|
|
|
NtDllIntercept.Init("ntdll.dll");
|
|
|
|
|
2011-11-19 03:26:00 +04:00
|
|
|
ReentrancySentinel::InitializeStatics();
|
|
|
|
|
2014-03-03 19:27:21 +04:00
|
|
|
// We specifically use a detour, because there are cases where external
|
|
|
|
// code also tries to hook LdrLoadDll, and doesn't know how to relocate our
|
|
|
|
// nop space patches. (Bug 951827)
|
2018-06-27 20:51:10 +03:00
|
|
|
bool ok = stub_LdrLoadDll.SetDetour(NtDllIntercept, "LdrLoadDll",
|
|
|
|
&patched_LdrLoadDll);
|
2009-11-04 09:35:20 +03:00
|
|
|
|
2013-11-12 17:31:32 +04:00
|
|
|
if (!ok) {
|
|
|
|
sBlocklistInitFailed = true;
|
2009-11-11 01:34:41 +03:00
|
|
|
#ifdef DEBUG
|
2017-01-12 05:13:40 +03:00
|
|
|
printf_stderr("LdrLoadDll hook failed, no dll blocklisting active\n");
|
2009-11-11 01:34:41 +03:00
|
|
|
#endif
|
2013-11-12 17:31:32 +04:00
|
|
|
}
|
2017-05-18 23:15:29 +03:00
|
|
|
|
2017-10-19 22:49:44 +03:00
|
|
|
// If someone injects a thread early that causes user32.dll to load off the
|
|
|
|
// main thread this causes issues, so load it as soon as we've initialized
|
|
|
|
// the block-list. (See bug 1400637)
|
|
|
|
if (!sUser32BeforeBlocklist) {
|
|
|
|
::LoadLibraryW(L"user32.dll");
|
|
|
|
}
|
|
|
|
|
2017-05-18 23:15:29 +03:00
|
|
|
Kernel32Intercept.Init("kernel32.dll");
|
|
|
|
|
|
|
|
#ifdef _M_AMD64
|
|
|
|
if (!IsWin8OrLater()) {
|
|
|
|
// The crash that this hook works around is only seen on Win7.
|
2018-06-27 20:51:10 +03:00
|
|
|
stub_RtlInstallFunctionTableCallback.Set(Kernel32Intercept,
|
|
|
|
"RtlInstallFunctionTableCallback",
|
|
|
|
&patched_RtlInstallFunctionTableCallback);
|
2017-05-18 23:15:29 +03:00
|
|
|
}
|
|
|
|
#endif
|
2017-05-24 12:56:40 +03:00
|
|
|
|
2017-08-17 16:05:17 +03:00
|
|
|
// Bug 1361410: WRusr.dll will overwrite our hook and cause a crash.
|
|
|
|
// Workaround: If we detect WRusr.dll, don't hook.
|
|
|
|
if (!GetModuleHandleW(L"WRusr.dll")) {
|
2018-06-27 20:51:10 +03:00
|
|
|
if (!stub_BaseThreadInitThunk.SetDetour(Kernel32Intercept,
|
|
|
|
"BaseThreadInitThunk",
|
|
|
|
&patched_BaseThreadInitThunk)) {
|
2017-05-24 12:56:40 +03:00
|
|
|
#ifdef DEBUG
|
2018-06-06 00:16:13 +03:00
|
|
|
printf_stderr("BaseThreadInitThunk hook failed\n");
|
2017-05-24 12:56:40 +03:00
|
|
|
#endif
|
2017-08-17 16:05:17 +03:00
|
|
|
}
|
2017-05-24 12:56:40 +03:00
|
|
|
}
|
2018-02-22 15:41:15 +03:00
|
|
|
|
|
|
|
#if defined(NIGHTLY_BUILD)
|
|
|
|
// Populate a list of thread start addresses to block.
|
|
|
|
HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
|
|
|
|
if (hKernel) {
|
|
|
|
void* pProc;
|
|
|
|
|
|
|
|
pProc = (void*)GetProcAddress(hKernel, "LoadLibraryA");
|
|
|
|
if (pProc) {
|
2018-09-18 21:59:54 +03:00
|
|
|
Unused << gStartAddressesToBlock->append(pProc);
|
2018-02-22 15:41:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pProc = (void*)GetProcAddress(hKernel, "LoadLibraryW");
|
|
|
|
if (pProc) {
|
2018-09-18 21:59:54 +03:00
|
|
|
Unused << gStartAddressesToBlock->append(pProc);
|
2018-02-22 15:41:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExA");
|
|
|
|
if (pProc) {
|
2018-09-18 21:59:54 +03:00
|
|
|
Unused << gStartAddressesToBlock->append(pProc);
|
2018-02-22 15:41:15 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExW");
|
|
|
|
if (pProc) {
|
2018-09-18 21:59:54 +03:00
|
|
|
Unused << gStartAddressesToBlock->append(pProc);
|
2018-02-22 15:41:15 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2013-11-12 17:31:32 +04:00
|
|
|
}
|
2011-09-19 23:49:30 +04:00
|
|
|
|
2018-08-29 21:49:49 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
MFBT_API void
|
|
|
|
DllBlocklist_Shutdown()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|
|
|
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
This introduces the machinery needed to generate crash annotations from a YAML
file. The relevant C++ functions are updated to take a typed enum. JavaScript
calls are unaffected but they will throw if the string argument does not
correspond to one of the known entries in the C++ enum. The existing whitelists
and blacklists of annotations are also generated from the YAML file and all
duplicate code related to them has been consolidated. Once written out to the
.extra file the annotations are converted in string form and are no different
than the existing ones.
All existing annotations have been included in the list (and some obsolete ones
have been removed) and all call sites have been updated including tests where
appropriate.
--HG--
extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
2018-07-05 16:42:11 +03:00
|
|
|
static void
|
|
|
|
WriteAnnotation(HANDLE aFile, Annotation aAnnotation, const char* aValue,
|
|
|
|
DWORD* aNumBytes)
|
|
|
|
{
|
|
|
|
const char* str = AnnotationToString(aAnnotation);
|
|
|
|
WriteFile(aFile, str, strlen(str), aNumBytes, nullptr);
|
|
|
|
WriteFile(aFile, "=", 1, aNumBytes, nullptr);
|
|
|
|
WriteFile(aFile, aValue, strlen(aValue), aNumBytes, nullptr);
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
static void
|
|
|
|
InternalWriteNotes(HANDLE file)
|
2013-10-18 22:24:50 +04:00
|
|
|
{
|
2013-11-12 17:31:32 +04:00
|
|
|
DWORD nBytes;
|
|
|
|
|
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
This introduces the machinery needed to generate crash annotations from a YAML
file. The relevant C++ functions are updated to take a typed enum. JavaScript
calls are unaffected but they will throw if the string argument does not
correspond to one of the known entries in the C++ enum. The existing whitelists
and blacklists of annotations are also generated from the YAML file and all
duplicate code related to them has been consolidated. Once written out to the
.extra file the annotations are converted in string form and are no different
than the existing ones.
All existing annotations have been included in the list (and some obsolete ones
have been removed) and all call sites have been updated including tests where
appropriate.
--HG--
extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
2018-07-05 16:42:11 +03:00
|
|
|
WriteAnnotation(file, Annotation::BlockedDllList, "", &nBytes);
|
2013-10-18 22:24:50 +04:00
|
|
|
DllBlockSet::Write(file);
|
2013-11-12 17:31:32 +04:00
|
|
|
WriteFile(file, "\n", 1, &nBytes, nullptr);
|
|
|
|
|
|
|
|
if (sBlocklistInitFailed) {
|
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
This introduces the machinery needed to generate crash annotations from a YAML
file. The relevant C++ functions are updated to take a typed enum. JavaScript
calls are unaffected but they will throw if the string argument does not
correspond to one of the known entries in the C++ enum. The existing whitelists
and blacklists of annotations are also generated from the YAML file and all
duplicate code related to them has been consolidated. Once written out to the
.extra file the annotations are converted in string form and are no different
than the existing ones.
All existing annotations have been included in the list (and some obsolete ones
have been removed) and all call sites have been updated including tests where
appropriate.
--HG--
extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
2018-07-05 16:42:11 +03:00
|
|
|
WriteAnnotation(file, Annotation::BlocklistInitFailed, "1\n", &nBytes);
|
2013-11-12 17:31:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sUser32BeforeBlocklist) {
|
Bug 1348273 - Convert crash annotations into a machine-readable list of constants; r=ted.mielczarek,njn,dholbert,mak,cpearce,mcmanus,froydnj,Dexter,jrmuizel,jchen,jimm,bz,surkov
This introduces the machinery needed to generate crash annotations from a YAML
file. The relevant C++ functions are updated to take a typed enum. JavaScript
calls are unaffected but they will throw if the string argument does not
correspond to one of the known entries in the C++ enum. The existing whitelists
and blacklists of annotations are also generated from the YAML file and all
duplicate code related to them has been consolidated. Once written out to the
.extra file the annotations are converted in string form and are no different
than the existing ones.
All existing annotations have been included in the list (and some obsolete ones
have been removed) and all call sites have been updated including tests where
appropriate.
--HG--
extra : source : 4f6c43f2830701ec5552e08e3f1b06fe6d045860
2018-07-05 16:42:11 +03:00
|
|
|
WriteAnnotation(file, Annotation::User32BeforeBlocklist, "1\n", &nBytes);
|
2013-11-12 17:31:32 +04:00
|
|
|
}
|
2013-10-18 22:24:50 +04:00
|
|
|
}
|
2016-07-27 23:20:58 +03:00
|
|
|
|
2018-06-06 00:16:13 +03:00
|
|
|
using WriterFn = void (*)(HANDLE);
|
|
|
|
static WriterFn gWriterFn = &InternalWriteNotes;
|
|
|
|
|
|
|
|
static void
|
|
|
|
GetNativeNtBlockSetWriter()
|
|
|
|
{
|
|
|
|
auto nativeWriter = reinterpret_cast<WriterFn>(
|
|
|
|
::GetProcAddress(::GetModuleHandleW(nullptr), "NativeNtBlockSet_Write"));
|
|
|
|
if (nativeWriter) {
|
|
|
|
gWriterFn = nativeWriter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MFBT_API void
|
|
|
|
DllBlocklist_WriteNotes(HANDLE file)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(gWriterFn);
|
|
|
|
gWriterFn(file);
|
|
|
|
}
|
|
|
|
|
2016-07-27 23:20:58 +03:00
|
|
|
MFBT_API bool
|
|
|
|
DllBlocklist_CheckStatus()
|
|
|
|
{
|
|
|
|
if (sBlocklistInitFailed || sUser32BeforeBlocklist)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2017-12-05 04:08:17 +03:00
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// This section is for DLL Services
|
|
|
|
// ============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
static SRWLOCK gDllServicesLock = SRWLOCK_INIT;
|
2018-01-31 00:23:10 +03:00
|
|
|
static mozilla::glue::detail::DllServicesBase* gDllServices;
|
2017-12-05 04:08:17 +03:00
|
|
|
|
|
|
|
class MOZ_RAII AutoSharedLock final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit AutoSharedLock(SRWLOCK& aLock)
|
|
|
|
: mLock(aLock)
|
|
|
|
{
|
|
|
|
::AcquireSRWLockShared(&aLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoSharedLock()
|
|
|
|
{
|
|
|
|
::ReleaseSRWLockShared(&mLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoSharedLock(const AutoSharedLock&) = delete;
|
|
|
|
AutoSharedLock(AutoSharedLock&&) = delete;
|
|
|
|
AutoSharedLock& operator=(const AutoSharedLock&) = delete;
|
|
|
|
AutoSharedLock& operator=(AutoSharedLock&&) = delete;
|
|
|
|
|
|
|
|
private:
|
|
|
|
SRWLOCK& mLock;
|
|
|
|
};
|
|
|
|
|
|
|
|
class MOZ_RAII AutoExclusiveLock final
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit AutoExclusiveLock(SRWLOCK& aLock)
|
|
|
|
: mLock(aLock)
|
|
|
|
{
|
|
|
|
::AcquireSRWLockExclusive(&aLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoExclusiveLock()
|
|
|
|
{
|
|
|
|
::ReleaseSRWLockExclusive(&mLock);
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoExclusiveLock(const AutoExclusiveLock&) = delete;
|
|
|
|
AutoExclusiveLock(AutoExclusiveLock&&) = delete;
|
|
|
|
AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
|
|
|
|
AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
|
|
|
|
|
|
|
|
private:
|
|
|
|
SRWLOCK& mLock;
|
|
|
|
};
|
|
|
|
|
|
|
|
// These types are documented on MSDN but not provided in any SDK headers
|
|
|
|
|
|
|
|
enum DllNotificationReason
|
|
|
|
{
|
|
|
|
LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
|
|
|
|
LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
|
|
|
|
ULONG Flags; //Reserved.
|
|
|
|
PCUNICODE_STRING FullDllName; //The full path name of the DLL module.
|
|
|
|
PCUNICODE_STRING BaseDllName; //The base file name of the DLL module.
|
|
|
|
PVOID DllBase; //A pointer to the base address for the DLL in memory.
|
|
|
|
ULONG SizeOfImage; //The size of the DLL image, in bytes.
|
|
|
|
} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
|
|
|
|
|
|
|
|
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
|
|
|
|
ULONG Flags; //Reserved.
|
|
|
|
PCUNICODE_STRING FullDllName; //The full path name of the DLL module.
|
|
|
|
PCUNICODE_STRING BaseDllName; //The base file name of the DLL module.
|
|
|
|
PVOID DllBase; //A pointer to the base address for the DLL in memory.
|
|
|
|
ULONG SizeOfImage; //The size of the DLL image, in bytes.
|
|
|
|
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
|
|
|
|
|
|
|
|
typedef union _LDR_DLL_NOTIFICATION_DATA {
|
|
|
|
LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
|
|
|
|
LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
|
|
|
|
} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
|
|
|
|
|
|
|
|
typedef const LDR_DLL_NOTIFICATION_DATA* PCLDR_DLL_NOTIFICATION_DATA;
|
|
|
|
|
|
|
|
typedef VOID (CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)(
|
|
|
|
ULONG aReason,
|
|
|
|
PCLDR_DLL_NOTIFICATION_DATA aNotificationData,
|
|
|
|
PVOID aContext);
|
|
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
|
|
LdrRegisterDllNotification(ULONG aFlags,
|
|
|
|
PLDR_DLL_NOTIFICATION_FUNCTION aCallback,
|
|
|
|
PVOID aContext, PVOID* aCookie);
|
|
|
|
|
|
|
|
static PVOID gNotificationCookie;
|
|
|
|
|
|
|
|
static VOID CALLBACK
|
|
|
|
DllLoadNotification(ULONG aReason, PCLDR_DLL_NOTIFICATION_DATA aNotificationData,
|
|
|
|
PVOID aContext)
|
|
|
|
{
|
|
|
|
if (aReason != LDR_DLL_NOTIFICATION_REASON_LOADED) {
|
|
|
|
// We don't care about unloads
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AutoSharedLock lock(gDllServicesLock);
|
|
|
|
if (!gDllServices) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
PCUNICODE_STRING fullDllName = aNotificationData->Loaded.FullDllName;
|
|
|
|
gDllServices->DispatchDllLoadNotification(fullDllName);
|
|
|
|
}
|
|
|
|
|
2018-01-31 01:08:03 +03:00
|
|
|
namespace mozilla {
|
|
|
|
Authenticode* GetAuthenticode();
|
|
|
|
} // namespace mozilla
|
|
|
|
|
2017-12-05 04:08:17 +03:00
|
|
|
MFBT_API void
|
2018-01-31 00:23:10 +03:00
|
|
|
DllBlocklist_SetDllServices(mozilla::glue::detail::DllServicesBase* aSvc)
|
2017-12-05 04:08:17 +03:00
|
|
|
{
|
|
|
|
AutoExclusiveLock lock(gDllServicesLock);
|
|
|
|
|
2018-01-31 01:08:03 +03:00
|
|
|
if (aSvc) {
|
|
|
|
aSvc->SetAuthenticodeImpl(GetAuthenticode());
|
|
|
|
|
|
|
|
if (!gNotificationCookie) {
|
|
|
|
auto pLdrRegisterDllNotification =
|
|
|
|
reinterpret_cast<decltype(&::LdrRegisterDllNotification)>(
|
|
|
|
::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"),
|
|
|
|
"LdrRegisterDllNotification"));
|
2018-01-31 01:08:03 +03:00
|
|
|
|
2018-01-31 01:08:03 +03:00
|
|
|
MOZ_DIAGNOSTIC_ASSERT(pLdrRegisterDllNotification);
|
2018-01-31 01:08:03 +03:00
|
|
|
|
2018-01-31 01:08:03 +03:00
|
|
|
NTSTATUS ntStatus = pLdrRegisterDllNotification(0, &DllLoadNotification,
|
|
|
|
nullptr, &gNotificationCookie);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(NT_SUCCESS(ntStatus));
|
|
|
|
}
|
2017-12-05 04:08:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
gDllServices = aSvc;
|
|
|
|
}
|
|
|
|
|