Bug 927944 - Annotate crash reports with a list of DLLs that were blocked via the windows blocklist. Also fix the blocklist to recognize forward-slash as a path delimiter, r=ehsan

--HG--
extra : rebase_source : 5cbff13357fe96c5a86b5511a58b69176aa71817
This commit is contained in:
Benjamin Smedberg 2013-10-18 14:24:50 -04:00
parent 0a63812eb6
commit b5399b893f
3 changed files with 127 additions and 6 deletions

View File

@ -222,6 +222,11 @@ static const char kIsGarbageCollectingParameter[] = "IsGarbageCollecting=";
static const int kIsGarbageCollectingParameterLen = static const int kIsGarbageCollectingParameterLen =
sizeof(kIsGarbageCollectingParameter)-1; sizeof(kIsGarbageCollectingParameter)-1;
#ifdef XP_WIN
static const char kBlockedDllsParameter[] = "BlockedDllList=";
static const int kBlockedDllsParameterLen = sizeof(kBlockedDllsParameter) - 1;
#endif
// this holds additional data sent via the API // this holds additional data sent via the API
static Mutex* crashReporterAPILock; static Mutex* crashReporterAPILock;
static Mutex* notesFieldLock; static Mutex* notesFieldLock;
@ -540,6 +545,9 @@ bool MinidumpCallback(
WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, nullptr); WriteFile(hFile, isGarbageCollecting ? "1" : "0", 1, &nBytes, nullptr);
WriteFile(hFile, "\n", 1, &nBytes, nullptr); WriteFile(hFile, "\n", 1, &nBytes, nullptr);
} }
WriteFile(hFile, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
WriteBlockedDlls(hFile);
WriteFile(hFile, "\n", 1, &nBytes, nullptr);
// Try to get some information about memory. // Try to get some information about memory.
MEMORYSTATUSEX statex; MEMORYSTATUSEX statex;

View File

@ -44,6 +44,11 @@ nsresult AppendAppNotesToCrashReport(const nsACString& data);
nsresult SetGarbageCollecting(bool collecting); nsresult SetGarbageCollecting(bool collecting);
#ifdef XP_WIN
// Implemented by the blocklist, this method writes the blocklist annotation
void WriteBlockedDlls(HANDLE file);
#endif
nsresult SetRestartArgs(int argc, char** argv); nsresult SetRestartArgs(int argc, char** argv);
nsresult SetupExtraData(nsIFile* aAppDataDirectory, nsresult SetupExtraData(nsIFile* aAppDataDirectory,
const nsACString& aBuildID); const nsACString& aBuildID);

View File

@ -23,7 +23,7 @@
using namespace mozilla; using namespace mozilla;
#if defined(MOZ_CRASHREPORTER) && !defined(NO_BLOCKLIST_CRASHREPORTER) #ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h" #include "nsExceptionHandler.h"
#endif #endif
@ -224,6 +224,10 @@ CheckASLR(const wchar_t* path)
return retval; return retval;
} }
// This lock protects both the reentrancy sentinel and the crash reporter
// data structures.
static CRITICAL_SECTION sLock;
/** /**
* Some versions of Windows call LoadLibraryEx to get the version information * Some versions of Windows call LoadLibraryEx to get the version information
* for a DLL, which causes our patched LdrLoadDll implementation to re-enter * for a DLL, which causes our patched LdrLoadDll implementation to re-enter
@ -270,16 +274,98 @@ public:
} }
private: private:
static CRITICAL_SECTION sLock;
static std::map<DWORD, const char*>* sThreadMap; static std::map<DWORD, const char*>* sThreadMap;
const char* mPreviousDllName; const char* mPreviousDllName;
bool mReentered; bool mReentered;
}; };
CRITICAL_SECTION ReentrancySentinel::sLock;
std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap; std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
/**
* 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)
{
}
const char* mName; // points into the sWindowsDllBlocklist string
unsigned long long mVersion;
DllBlockSet* mNext;
static DllBlockSet* gFirst;
};
DllBlockSet* DllBlockSet::gFirst;
void
DllBlockSet::Add(const char* name, unsigned long long version)
{
EnterCriticalSection(&sLock);
for (DllBlockSet* b = gFirst; b; b = b->mNext) {
if (0 == strcmp(b->mName, name) && b->mVersion == version) {
LeaveCriticalSection(&sLock);
return;
}
}
// Not already present
DllBlockSet* n = new DllBlockSet(name, version);
n->mNext = gFirst;
gFirst = n;
LeaveCriticalSection(&sLock);
}
void
DllBlockSet::Write(HANDLE file)
{
EnterCriticalSection(&sLock);
DWORD nBytes;
// Because this method is called after a crash occurs, and uses heap memory,
// protect this entire block with a structured exception handler.
__try {
for (DllBlockSet* b = gFirst; b; b = b->mNext) {
// write name[,v.v.v.v];
WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
if (b->mVersion != -1) {
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);
}
}
__except (EXCEPTION_EXECUTE_HANDLER) { }
LeaveCriticalSection(&sLock);
}
static static
wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname) wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname)
{ {
@ -308,6 +394,17 @@ wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname)
return full_fname; return full_fname;
} }
// No builtin function to find the last character matching a set
static wchar_t* lastslash(wchar_t* s, int len)
{
for (wchar_t* c = s + len - 1; c >= s; --c) {
if (*c == L'\\' || *c == L'/') {
return c;
}
}
return nullptr;
}
static NTSTATUS NTAPI static NTSTATUS NTAPI
patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle) patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
{ {
@ -334,7 +431,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
goto continue_loading; goto continue_loading;
} }
dll_part = wcsrchr(fname, L'\\'); dll_part = lastslash(fname, len);
if (dll_part) { if (dll_part) {
dll_part = dll_part + 1; dll_part = dll_part + 1;
len -= dll_part - fname; len -= dll_part - fname;
@ -400,6 +497,8 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
goto continue_loading; goto continue_loading;
} }
unsigned long long fVersion = ALL_VERSIONS;
if (info->maxVersion != ALL_VERSIONS) { if (info->maxVersion != ALL_VERSIONS) {
ReentrancySentinel sentinel(dllName); ReentrancySentinel sentinel(dllName);
if (sentinel.BailOut()) { if (sentinel.BailOut()) {
@ -426,7 +525,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) && if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) &&
VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen)) VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen))
{ {
unsigned long long fVersion = fVersion =
((unsigned long long)vInfo->dwFileVersionMS) << 32 | ((unsigned long long)vInfo->dwFileVersionMS) << 32 |
((unsigned long long)vInfo->dwFileVersionLS); ((unsigned long long)vInfo->dwFileVersionLS);
@ -440,6 +539,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
if (!load_ok) { if (!load_ok) {
printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName); 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; return STATUS_DLL_NOT_FOUND;
} }
} }
@ -485,9 +585,17 @@ XRE_SetupDllBlocklist()
printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n"); printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n");
#endif #endif
#if defined(MOZ_CRASHREPORTER) && !defined(NO_BLOCKLIST_CRASHREPORTER) #ifdef MOZ_CRASHREPORTER
if (!ok) { if (!ok) {
CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("DllBlockList Failed\n")); CrashReporter::AppendAppNotesToCrashReport(NS_LITERAL_CSTRING("DllBlockList Failed\n"));
} }
#endif #endif
} }
#ifdef MOZ_CRASHREPORTER
void
CrashReporter::WriteBlockedDlls(HANDLE file)
{
DllBlockSet::Write(file);
}
#endif