diff --git a/b2g/Makefile.in b/b2g/Makefile.in index d74045d016a..6fa4ddc4370 100644 --- a/b2g/Makefile.in +++ b/b2g/Makefile.in @@ -9,11 +9,13 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS = chrome components locales app +DIRS = chrome components locales ifeq ($(OS_ARCH),WINNT) DIRS += $(DEPTH)/xulrunner/tools/redit endif +DIRS += app + include $(topsrcdir)/config/rules.mk include $(topsrcdir)/testing/testsuite-targets.mk diff --git a/b2g/config/mozconfigs/linux32/debug b/b2g/config/mozconfigs/linux32/debug deleted file mode 100644 index b53abb687d1..00000000000 --- a/b2g/config/mozconfigs/linux32/debug +++ /dev/null @@ -1,23 +0,0 @@ -#GONK_TOOLCHAIN_VERSION=0 -#TOOLCHAIN_HOST=linux-x86 -#export GONK_PRODUCT=generic -#gonk="/home/cjones/mozilla/gonk-toolchain-$GONK_TOOLCHAIN_VERSION" - -mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-b2g - -mk_add_options MOZ_MAKE_FLAGS="-j8" - -ac_add_options --enable-application=b2g - -ac_add_options --target=arm-android-eabi -ac_add_options --with-gonk="$gonk" -ac_add_options --with-gonk-toolchain-prefix="$gonk/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-eabi-4.4.3/bin/arm-eabi-" -ac_add_options --with-endian=little -ac_add_options --disable-elf-hack -ac_add_options --enable-debug-symbols -ac_add_options --enable-profiling -ac_add_options --with-ccache -ac_add_options --enable-marionette - -# Enable dump() from JS. -export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP diff --git a/b2g/config/mozconfigs/linux32/nightly b/b2g/config/mozconfigs/linux32/nightly deleted file mode 100644 index b53abb687d1..00000000000 --- a/b2g/config/mozconfigs/linux32/nightly +++ /dev/null @@ -1,23 +0,0 @@ -#GONK_TOOLCHAIN_VERSION=0 -#TOOLCHAIN_HOST=linux-x86 -#export GONK_PRODUCT=generic -#gonk="/home/cjones/mozilla/gonk-toolchain-$GONK_TOOLCHAIN_VERSION" - -mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-b2g - -mk_add_options MOZ_MAKE_FLAGS="-j8" - -ac_add_options --enable-application=b2g - -ac_add_options --target=arm-android-eabi -ac_add_options --with-gonk="$gonk" -ac_add_options --with-gonk-toolchain-prefix="$gonk/prebuilt/$TOOLCHAIN_HOST/toolchain/arm-eabi-4.4.3/bin/arm-eabi-" -ac_add_options --with-endian=little -ac_add_options --disable-elf-hack -ac_add_options --enable-debug-symbols -ac_add_options --enable-profiling -ac_add_options --with-ccache -ac_add_options --enable-marionette - -# Enable dump() from JS. -export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP diff --git a/b2g/config/mozconfigs/desktop/linux32/nightly b/b2g/config/mozconfigs/linux32_gecko/nightly similarity index 96% rename from b2g/config/mozconfigs/desktop/linux32/nightly rename to b2g/config/mozconfigs/linux32_gecko/nightly index 0d90c807961..63fac232d7e 100644 --- a/b2g/config/mozconfigs/desktop/linux32/nightly +++ b/b2g/config/mozconfigs/linux32_gecko/nightly @@ -6,8 +6,6 @@ ac_add_options --enable-signmar # Nightlies only since this has a cost in performance #ac_add_options --enable-js-diagnostics -. $topsrcdir/build/unix/mozconfig.linux - # Avoid dependency on libstdc++ 4.5 ac_add_options --enable-stdcxx-compat diff --git a/b2g/config/mozconfigs/desktop/linux64/nightly b/b2g/config/mozconfigs/linux64_gecko/nightly similarity index 100% rename from b2g/config/mozconfigs/desktop/linux64/nightly rename to b2g/config/mozconfigs/linux64_gecko/nightly diff --git a/b2g/config/mozconfigs/desktop/macosx64/nightly b/b2g/config/mozconfigs/macosx64_gecko/nightly similarity index 100% rename from b2g/config/mozconfigs/desktop/macosx64/nightly rename to b2g/config/mozconfigs/macosx64_gecko/nightly diff --git a/b2g/config/mozconfigs/desktop/win32/nightly b/b2g/config/mozconfigs/win32_gecko/nightly similarity index 100% rename from b2g/config/mozconfigs/desktop/win32/nightly rename to b2g/config/mozconfigs/win32_gecko/nightly diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 2422066f573..1b0257ec0d6 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -661,6 +661,9 @@ bin/libfreebl_32int64_3.so #endif #endif @BINPATH@/crashreporter-override.ini +#ifdef MOZ_CRASHREPORTER_INJECTOR +@BINPATH@/breakpadinjector.dll +#endif #endif ; [Extensions] diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index a995be6a968..ae3345fb09d 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -155,6 +155,7 @@ LIBJPEG_TURBO_ARM_ASM = @LIBJPEG_TURBO_ARM_ASM@ NS_PRINTING = @NS_PRINTING@ MOZ_PDF_PRINTING = @MOZ_PDF_PRINTING@ MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@ +MOZ_CRASHREPORTER_INJECTOR = @MOZ_CRASHREPORTER_INJECTOR@ MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@ MOC = @MOC@ RCC = @RCC@ diff --git a/configure.in b/configure.in index d177943ce2a..6d124b2c91c 100644 --- a/configure.in +++ b/configure.in @@ -5858,6 +5858,11 @@ if test -n "$MOZ_CRASHREPORTER"; then if (test "$OS_ARCH" != "$HOST_OS_ARCH"); then AC_MSG_ERROR([Breakpad tools do not support compiling on $HOST_OS_ARCH while targeting $OS_ARCH. Use --disable-crashreporter.]) fi + + if test "$OS_ARCH" == "WINNT" -a -z "$HAVE_64BIT_OS"; then + MOZ_CRASHREPORTER_INJECTOR=1 + AC_DEFINE(MOZ_CRASHREPORTER_INJECTOR) + fi fi MOZ_ARG_WITH_STRING(crashreporter-enable-percent, @@ -8327,6 +8332,7 @@ AC_SUBST(MOZ_SPELLCHECK) AC_SUBST(MOZ_JAVA_COMPOSITOR) AC_SUBST(MOZ_ONLY_TOUCH_EVENTS) AC_SUBST(MOZ_CRASHREPORTER) +AC_SUBST(MOZ_CRASHREPORTER_INJECTOR) AC_SUBST(MOZ_MAINTENANCE_SERVICE) AC_SUBST(MOZ_VERIFY_MAR_SIGNATURE) AC_SUBST(MOZ_ENABLE_SIGNMAR) diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index bb7d4f227ed..a606f639817 100644 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -102,6 +102,10 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath) , mNPNIface(NULL) , mPlugin(NULL) , mTaskFactory(this) +#ifdef MOZ_CRASHREPORTER_INJECTOR + , mFlashProcess1(0) + , mFlashProcess2(0) +#endif { NS_ASSERTION(mSubprocess, "Out of memory!"); @@ -127,6 +131,13 @@ PluginModuleParent::~PluginModuleParent() mSubprocess = nsnull; } +#ifdef MOZ_CRASHREPORTER_INJECTOR + if (mFlashProcess1) + CrashReporter::UnregisterInjectorCallback(mFlashProcess1); + if (mFlashProcess2) + CrashReporter::UnregisterInjectorCallback(mFlashProcess2); +#endif + Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this); Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this); } @@ -151,9 +162,12 @@ PluginModuleParent::WriteExtraDataForMinidump(CrashReporter::AnnotationTable& no notes.Put(CS("PluginName"), CS("")); notes.Put(CS("PluginVersion"), CS("")); - const nsString& hangID = CrashReporter()->HangID(); - if (!hangID.IsEmpty()) - notes.Put(CS("HangID"), NS_ConvertUTF16toUTF8(hangID)); + CrashReporterParent* crashReporter = CrashReporter(); + if (crashReporter) { + const nsString& hangID = crashReporter->HangID(); + if (!hangID.IsEmpty()) + notes.Put(CS("HangID"), NS_ConvertUTF16toUTF8(hangID)); + } } #endif // MOZ_CRASHREPORTER @@ -186,17 +200,19 @@ bool PluginModuleParent::ShouldContinueFromReplyTimeout() { #ifdef MOZ_CRASHREPORTER - CrashReporterParent* crashReporter = CrashReporter(); - if (crashReporter->GeneratePairedMinidump(this)) { - mBrowserDumpID = crashReporter->ParentDumpID(); - mPluginDumpID = crashReporter->ChildDumpID(); - PLUGIN_LOG_DEBUG( - ("generated paired browser/plugin minidumps: %s/%s (ID=%s)", - NS_ConvertUTF16toUTF8(mBrowserDumpID).get(), - NS_ConvertUTF16toUTF8(mPluginDumpID).get(), - NS_ConvertUTF16toUTF8(crashReporter->HangID()).get())); - } else { - NS_WARNING("failed to capture paired minidumps from hang"); + if (mPluginDumpID.IsEmpty()) { + CrashReporterParent* crashReporter = CrashReporter(); + if (crashReporter->GeneratePairedMinidump(this)) { + mBrowserDumpID = crashReporter->ParentDumpID(); + mPluginDumpID = crashReporter->ChildDumpID(); + PLUGIN_LOG_DEBUG( + ("generated paired browser/plugin minidumps: %s/%s (ID=%s)", + NS_ConvertUTF16toUTF8(mBrowserDumpID).get(), + NS_ConvertUTF16toUTF8(mPluginDumpID).get(), + NS_ConvertUTF16toUTF8(crashReporter->HangID()).get())); + } else { + NS_WARNING("failed to capture paired minidumps from hang"); + } } #endif @@ -217,7 +233,6 @@ PluginModuleParent::ShouldContinueFromReplyTimeout() CrashReporterParent* PluginModuleParent::CrashReporter() { - MOZ_ASSERT(ManagedPCrashReporterParent().Length() > 0); return static_cast(ManagedPCrashReporterParent()[0]); } #endif @@ -234,14 +249,18 @@ PluginModuleParent::ActorDestroy(ActorDestroyReason why) notes.Init(4); WriteExtraDataForMinidump(notes); - if (crashReporter->GenerateCrashReport(this, ¬es)) { + if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) { + crashReporter->GenerateHangCrashReport(¬es); + } + else if (!mPluginDumpID.IsEmpty()) { + // Nothing to do, we've already written this minidump in + // PluginModuleParent::OnCrash + } + else if (crashReporter->GenerateCrashReport(this, ¬es)) { mPluginDumpID = crashReporter->ChildDumpID(); PLUGIN_LOG_DEBUG(("got child minidump: %s", NS_ConvertUTF16toUTF8(mPluginDumpID).get())); } - else if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) { - crashReporter->GenerateHangCrashReport(¬es); - } else { NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!"); } @@ -761,6 +780,10 @@ PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) unused << SendSetAudioSessionData(id, sessionName, iconPath); #endif +#ifdef MOZ_CRASHREPORTER_INJECTOR + InitializeInjector(); +#endif + return NS_OK; } #endif @@ -1162,3 +1185,102 @@ PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages) mozilla::plugins::parent::_reloadplugins(aReloadPages); return true; } + +#ifdef MOZ_CRASHREPORTER_INJECTOR + +// We only add the crash reporter to subprocess which have the filename +// FlashPlayerPlugin* +#define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN" + +static DWORD +GetFlashChildOfPID(DWORD pid, HANDLE snapshot) +{ + PROCESSENTRY32 entry = { + sizeof(entry) + }; + for (BOOL ok = Process32First(snapshot, &entry); + ok; + ok = Process32Next(snapshot, &entry)) { + if (entry.th32ParentProcessID == pid) { + nsString name(entry.szExeFile); + ToUpperCase(name); + if (StringBeginsWith(name, NS_LITERAL_STRING(FLASH_PROCESS_PREFIX))) { + return entry.th32ProcessID; + } + } + } + return 0; +} + +// We only look for child processes of the Flash plugin, NPSWF* +#define FLASH_PLUGIN_PREFIX "NPSWF" + +void +PluginModuleParent::InitializeInjector() +{ + if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false)) + return; + + nsCString path(Process()->GetPluginFilePath().c_str()); + ToUpperCase(path); + PRInt32 lastSlash = path.RFindCharInSet("\\/"); + if (kNotFound == lastSlash) + return; + + if (!StringBeginsWith(Substring(path, lastSlash + 1), + NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX))) + return; + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (INVALID_HANDLE_VALUE == snapshot) + return; + + DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle()); + mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, snapshot); + if (mFlashProcess1) { + InjectCrashReporterIntoProcess(mFlashProcess1, this); + + mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot); + if (mFlashProcess2) { + InjectCrashReporterIntoProcess(mFlashProcess2, this); + } + } +} + +void +PluginModuleParent::OnCrash(DWORD processID, const nsAString& aDumpID) +{ + if (!mPluginDumpID.IsEmpty()) { + // One process has already crashed: we assume that the first-to-crash + // is the interesting one + return; + } + + mPluginDumpID = aDumpID; + + CrashReporter::AnnotationTable notes; + notes.Init(4); + WriteExtraDataForMinidump(notes); + notes.Put(NS_LITERAL_CSTRING("ProcessType"), NS_LITERAL_CSTRING("plugin")); + if (processID == mFlashProcess1) { + notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), + NS_LITERAL_CSTRING("Broker")); + } + else if (processID == mFlashProcess2) { + notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), + NS_LITERAL_CSTRING("Sandbox")); + } + else { + NS_ERROR("Got minidump for Flash process neither broker nor sandbox."); + } + + CrashReporter::AppendExtraData(aDumpID, notes); + MessageLoop::current()->PostTask( + FROM_HERE, + mTaskFactory.NewRunnableMethod( + &PluginModuleParent::CleanupFromTimeout)); + + KillProcess(OtherProcess(), 1, false); +} + +#endif // MOZ_CRASHREPORTER_INJECTOR diff --git a/dom/plugins/ipc/PluginModuleParent.h b/dom/plugins/ipc/PluginModuleParent.h index 898054cef97..4f4af8e2e86 100644 --- a/dom/plugins/ipc/PluginModuleParent.h +++ b/dom/plugins/ipc/PluginModuleParent.h @@ -30,6 +30,10 @@ #include "nsHashKeys.h" #include "nsIFileStreams.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif + namespace mozilla { namespace dom { class PCrashReporterParent; @@ -52,7 +56,12 @@ class BrowserStreamParent; * child process needs to make these calls back into Gecko proper. * This class is responsible for "actually" making those function calls. */ -class PluginModuleParent : public PPluginModuleParent, PluginLibrary +class PluginModuleParent + : public PPluginModuleParent + , public PluginLibrary +#ifdef MOZ_CRASHREPORTER_INJECTOR + , public CrashReporter::InjectorCrashCallback +#endif { private: typedef mozilla::PluginLibrary PluginLibrary; @@ -306,6 +315,15 @@ private: #endif friend class mozilla::dom::CrashReporterParent; + +#ifdef MOZ_CRASHREPORTER_INJECTOR + void InitializeInjector(); + + NS_OVERRIDE void OnCrash(DWORD processID, const nsAString& aDumpID); + + DWORD mFlashProcess1; + DWORD mFlashProcess2; +#endif }; } // namespace plugins diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 0b87334cde3..823f91efddc 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1630,6 +1630,8 @@ pref("dom.ipc.plugins.parentTimeoutSecs", 0); // conflicts with our implementation, at least on Windows). pref("dom.ipc.plugins.java.enabled", false); +pref("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", true); + #ifndef ANDROID #ifndef XP_MACOSX #ifdef XP_UNIX diff --git a/toolkit/crashreporter/InjectCrashReporter.cpp b/toolkit/crashreporter/InjectCrashReporter.cpp new file mode 100644 index 00000000000..a695d28897b --- /dev/null +++ b/toolkit/crashreporter/InjectCrashReporter.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; 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/. */ + +#include "InjectCrashReporter.h" +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "client/windows/crash_generation/crash_generation_client.h" +#include "nsExceptionHandler.h" +#include "LoadLibraryRemote.h" +#include "nsWindowsHelpers.h" + +using google_breakpad::CrashGenerationClient; +using CrashReporter::GetChildNotificationPipe; + +namespace mozilla { + +InjectCrashRunnable::InjectCrashRunnable(DWORD pid) + : mPID(pid) +{ + nsCOMPtr dll; + nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(dll)); + if (NS_SUCCEEDED(rv)) { + dll->Append(NS_LITERAL_STRING("breakpadinjector.dll")); + dll->GetPath(mInjectorPath); + } +} + +NS_IMETHODIMP +InjectCrashRunnable::Run() +{ + if (mInjectorPath.IsEmpty()) + return NS_OK; + + nsAutoHandle hProcess( + OpenProcess(PROCESS_CREATE_THREAD | + PROCESS_QUERY_INFORMATION | + PROCESS_DUP_HANDLE | + PROCESS_VM_OPERATION | + PROCESS_VM_WRITE | + PROCESS_VM_READ, FALSE, mPID)); + if (!hProcess) { + NS_WARNING("Unable to open remote process handle for crashreporter injection."); + return NS_OK; + } + + void* proc = LoadRemoteLibraryAndGetAddress(hProcess, mInjectorPath.get(), + "Start"); + if (!proc) { + NS_WARNING("Unable to inject crashreporter DLL."); + return NS_OK; + } + + HANDLE hRemotePipe = + CrashGenerationClient::DuplicatePipeToClientProcess( + NS_ConvertASCIItoUTF16(GetChildNotificationPipe()).get(), + hProcess); + if (INVALID_HANDLE_VALUE == hRemotePipe) { + NS_WARNING("Unable to duplicate crash reporter pipe to process."); + return NS_OK; + } + + nsAutoHandle hThread(CreateRemoteThread(hProcess, NULL, 0, + (LPTHREAD_START_ROUTINE) proc, + (void*) hRemotePipe, 0, NULL)); + if (!hThread) { + NS_WARNING("Unable to CreateRemoteThread"); + + // We have to close the remote pipe or else our crash generation client + // will be stuck unable to accept other remote requests. + HANDLE toClose = INVALID_HANDLE_VALUE; + if (DuplicateHandle(hProcess, hRemotePipe, ::GetCurrentProcess(), + &toClose, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + CloseHandle(toClose); + return NS_OK; + } + } + + return NS_OK; +} + +} // namespace mozilla diff --git a/toolkit/crashreporter/InjectCrashReporter.h b/toolkit/crashreporter/InjectCrashReporter.h new file mode 100644 index 00000000000..c315277f5e6 --- /dev/null +++ b/toolkit/crashreporter/InjectCrashReporter.h @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; 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/. */ + +#include "nsThreadUtils.h" +#include + +namespace mozilla { + +class InjectCrashRunnable : public nsRunnable +{ +public: + InjectCrashRunnable(DWORD pid); + + NS_IMETHOD Run(); + +private: + DWORD mPID; + nsString mInjectorPath; +}; + +} // Namespace mozilla diff --git a/toolkit/crashreporter/LoadLibraryRemote.cpp b/toolkit/crashreporter/LoadLibraryRemote.cpp new file mode 100644 index 00000000000..30af4f845ab --- /dev/null +++ b/toolkit/crashreporter/LoadLibraryRemote.cpp @@ -0,0 +1,440 @@ +/* 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 __GNUC__ +// disable warnings about pointer <-> DWORD conversions +#pragma warning( disable : 4311 4312 ) +#endif + +#ifdef _WIN64 +#define POINTER_TYPE ULONGLONG +#else +#define POINTER_TYPE DWORD +#endif + +#include +#include +#include +#ifdef DEBUG_OUTPUT +#include +#endif + +#include "nsWindowsHelpers.h" + +namespace { + +typedef const unsigned char* FileView; + +template<> +class nsAutoRefTraits +{ +public: + typedef FileView RawRef; + static FileView Void() + { + return NULL; + } + + static void Release(RawRef aView) + { + if (NULL != aView) + UnmapViewOfFile(aView); + } +}; + +} // anonymous namespace + +#ifndef IMAGE_SIZEOF_BASE_RELOCATION +// Vista SDKs no longer define IMAGE_SIZEOF_BASE_RELOCATION!? +#define IMAGE_SIZEOF_BASE_RELOCATION (sizeof(IMAGE_BASE_RELOCATION)) +#endif + +#include "LoadLibraryRemote.h" + +typedef struct { + PIMAGE_NT_HEADERS headers; + unsigned char *localCodeBase; + unsigned char *remoteCodeBase; + HMODULE *modules; + int numModules; +} MEMORYMODULE, *PMEMORYMODULE; + +typedef BOOL (WINAPI *DllEntryProc)(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); + +#define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader.DataDirectory[idx] + +#ifdef DEBUG_OUTPUT +static void +OutputLastError(const char *msg) +{ + char* tmp; + char *tmpmsg; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &tmp, 0, NULL); + tmpmsg = (char *)LocalAlloc(LPTR, strlen(msg) + strlen(tmp) + 3); + sprintf(tmpmsg, "%s: %s", msg, tmp); + OutputDebugStringA(tmpmsg); + LocalFree(tmpmsg); + LocalFree(tmp); +} +#endif + +static void +CopySections(const unsigned char *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module) +{ + int i; + unsigned char *codeBase = module->localCodeBase; + unsigned char *dest; + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); + for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) { + dest = codeBase + section->VirtualAddress; + memset(dest, 0, section->Misc.VirtualSize); + if (section->SizeOfRawData) { + memcpy(dest, data + section->PointerToRawData, section->SizeOfRawData); + } + // section->Misc.PhysicalAddress = (POINTER_TYPE) module->remoteCodeBase + section->VirtualAddress; + } +} + +// Protection flags for memory pages (Executable, Readable, Writeable) +static int ProtectionFlags[2][2][2] = { + { + // not executable + {PAGE_NOACCESS, PAGE_WRITECOPY}, + {PAGE_READONLY, PAGE_READWRITE}, + }, { + // executable + {PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY}, + {PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE}, + }, +}; + +static bool +FinalizeSections(PMEMORYMODULE module, HANDLE hRemoteProcess) +{ +#ifdef DEBUG_OUTPUT + fprintf(stderr, "Finalizing sections: local base %p, remote base %p\n", + module->localCodeBase, module->remoteCodeBase); +#endif + + int i; + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); + + // loop through all sections and change access flags + for (i=0; iheaders->FileHeader.NumberOfSections; i++, section++) { + DWORD protect, oldProtect, size; + int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; + int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; + + // determine protection flags based on characteristics + protect = ProtectionFlags[executable][readable][writeable]; + if (section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED) { + protect |= PAGE_NOCACHE; + } + + // determine size of region + size = section->Misc.VirtualSize; + if (size > 0) { + void* remoteAddress = module->remoteCodeBase + section->VirtualAddress; + void* localAddress = module->localCodeBase + section->VirtualAddress; + +#ifdef DEBUG_OUTPUT + fprintf(stderr, "Copying section %s to %p, size %x, executable %i readable %i writeable %i\n", + section->Name, remoteAddress, size, executable, readable, writeable); +#endif + + // Copy the data from local->remote and set the memory protection + if (!VirtualAllocEx(hRemoteProcess, remoteAddress, size, MEM_COMMIT, PAGE_READWRITE)) + return false; + + if (!WriteProcessMemory(hRemoteProcess, + remoteAddress, + localAddress, + size, + NULL)) { +#ifdef DEBUG_OUTPUT + OutputLastError("Error writing remote memory.\n"); +#endif + return false; + } + + if (VirtualProtectEx(hRemoteProcess, remoteAddress, size, protect, &oldProtect) == 0) { +#ifdef DEBUG_OUTPUT + OutputLastError("Error protecting memory page"); +#endif + return false; + } + } + } + return true; +} + +static void +PerformBaseRelocation(PMEMORYMODULE module, SIZE_T delta) +{ + DWORD i; + unsigned char *codeBase = module->localCodeBase; + + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC); + if (directory->Size > 0) { + PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION) (codeBase + directory->VirtualAddress); + for (; relocation->VirtualAddress > 0; ) { + unsigned char *dest = codeBase + relocation->VirtualAddress; + unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION); + for (i=0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++) { + DWORD *patchAddrHL; +#ifdef _WIN64 + ULONGLONG *patchAddr64; +#endif + int type, offset; + + // the upper 4 bits define the type of relocation + type = *relInfo >> 12; + // the lower 12 bits define the offset + offset = *relInfo & 0xfff; + + switch (type) + { + case IMAGE_REL_BASED_ABSOLUTE: + // skip relocation + break; + + case IMAGE_REL_BASED_HIGHLOW: + // change complete 32 bit address + patchAddrHL = (DWORD *) (dest + offset); + *patchAddrHL += delta; + break; + +#ifdef _WIN64 + case IMAGE_REL_BASED_DIR64: + patchAddr64 = (ULONGLONG *) (dest + offset); + *patchAddr64 += delta; + break; +#endif + + default: + //printf("Unknown relocation: %d\n", type); + break; + } + } + + // advance to next relocation block + relocation = (PIMAGE_BASE_RELOCATION) (((char *) relocation) + relocation->SizeOfBlock); + } + } +} + +static int +BuildImportTable(PMEMORYMODULE module) +{ + int result=1; + unsigned char *codeBase = module->localCodeBase; + + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_IMPORT); + if (directory->Size > 0) { + PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress); + PIMAGE_IMPORT_DESCRIPTOR importEnd = (PIMAGE_IMPORT_DESCRIPTOR) (codeBase + directory->VirtualAddress + directory->Size); + + for (; importDesc < importEnd && importDesc->Name; importDesc++) { + POINTER_TYPE *thunkRef; + FARPROC *funcRef; + HMODULE handle = GetModuleHandleA((LPCSTR) (codeBase + importDesc->Name)); + if (handle == NULL) { +#if DEBUG_OUTPUT + OutputLastError("Can't load library"); +#endif + result = 0; + break; + } + + module->modules = (HMODULE *)realloc(module->modules, (module->numModules+1)*(sizeof(HMODULE))); + if (module->modules == NULL) { + result = 0; + break; + } + + module->modules[module->numModules++] = handle; + if (importDesc->OriginalFirstThunk) { + thunkRef = (POINTER_TYPE *) (codeBase + importDesc->OriginalFirstThunk); + funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); + } else { + // no hint table + thunkRef = (POINTER_TYPE *) (codeBase + importDesc->FirstThunk); + funcRef = (FARPROC *) (codeBase + importDesc->FirstThunk); + } + for (; *thunkRef; thunkRef++, funcRef++) { + if (IMAGE_SNAP_BY_ORDINAL(*thunkRef)) { + *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)IMAGE_ORDINAL(*thunkRef)); + } else { + PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME) (codeBase + (*thunkRef)); + *funcRef = (FARPROC)GetProcAddress(handle, (LPCSTR)&thunkData->Name); + } + if (*funcRef == 0) { + result = 0; + break; + } + } + + if (!result) { + break; + } + } + } + + return result; +} + +static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name); + +void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess, + const WCHAR* library, + const char* symbol) +{ + // Map the DLL into memory + nsAutoHandle hLibrary( + CreateFile(library, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL)); + if (INVALID_HANDLE_VALUE == hLibrary) { +#if DEBUG_OUTPUT + OutputLastError("Couldn't CreateFile the library.\n"); +#endif + return NULL; + } + + nsAutoHandle hMapping( + CreateFileMapping(hLibrary, NULL, PAGE_READONLY, 0, 0, NULL)); + if (!hMapping) { +#if DEBUG_OUTPUT + OutputLastError("Couldn't CreateFileMapping.\n"); +#endif + return NULL; + } + + nsAutoRef data( + (const unsigned char*) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0)); + if (!data) { +#if DEBUG_OUTPUT + OutputLastError("Couldn't MapViewOfFile.\n"); +#endif + return NULL; + } + + SIZE_T locationDelta; + + PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)data.get(); + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { +#if DEBUG_OUTPUT + OutputDebugStringA("Not a valid executable file.\n"); +#endif + return NULL; + } + + PIMAGE_NT_HEADERS old_header = (PIMAGE_NT_HEADERS)(data + dos_header->e_lfanew); + if (old_header->Signature != IMAGE_NT_SIGNATURE) { +#if DEBUG_OUTPUT + OutputDebugStringA("No PE header found.\n"); +#endif + return NULL; + } + + // reserve memory for image of library in this process and the target process + unsigned char* localCode = (unsigned char*) VirtualAlloc(NULL, + old_header->OptionalHeader.SizeOfImage, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE); + if (!localCode) { +#if DEBUG_OUTPUT + OutputLastError("Can't reserve local memory."); +#endif + } + + unsigned char* remoteCode = (unsigned char*) VirtualAllocEx(hRemoteProcess, NULL, + old_header->OptionalHeader.SizeOfImage, + MEM_RESERVE, + PAGE_EXECUTE_READ); + if (!remoteCode) { +#if DEBUG_OUTPUT + OutputLastError("Can't reserve remote memory."); +#endif + } + + MEMORYMODULE result; + result.localCodeBase = localCode; + result.remoteCodeBase = remoteCode; + result.numModules = 0; + result.modules = NULL; + + // copy PE header to code + memcpy(localCode, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders); + result.headers = reinterpret_cast(localCode + dos_header->e_lfanew); + + // update position + result.headers->OptionalHeader.ImageBase = (POINTER_TYPE)remoteCode; + + // copy sections from DLL file block to new memory location + CopySections(data, old_header, &result); + + // adjust base address of imported data + locationDelta = (SIZE_T)(remoteCode - old_header->OptionalHeader.ImageBase); + if (locationDelta != 0) { + PerformBaseRelocation(&result, locationDelta); + } + + // load required dlls and adjust function table of imports + if (!BuildImportTable(&result)) { + return NULL; + } + + // mark memory pages depending on section headers and release + // sections that are marked as "discardable" + if (!FinalizeSections(&result, hRemoteProcess)) { + return NULL; + } + + return MemoryGetProcAddress(&result, symbol); +} + +static void* MemoryGetProcAddress(PMEMORYMODULE module, const char *name) +{ + unsigned char *localCodeBase = module->localCodeBase; + int idx=-1; + DWORD i, *nameRef; + WORD *ordinal; + PIMAGE_EXPORT_DIRECTORY exports; + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_EXPORT); + if (directory->Size == 0) { + // no export table found + return NULL; + } + + exports = (PIMAGE_EXPORT_DIRECTORY) (localCodeBase + directory->VirtualAddress); + if (exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0) { + // DLL doesn't export anything + return NULL; + } + + // search function name in list of exported names + nameRef = (DWORD *) (localCodeBase + exports->AddressOfNames); + ordinal = (WORD *) (localCodeBase + exports->AddressOfNameOrdinals); + for (i=0; iNumberOfNames; i++, nameRef++, ordinal++) { + if (stricmp(name, (const char *) (localCodeBase + (*nameRef))) == 0) { + idx = *ordinal; + break; + } + } + + if (idx == -1) { + // exported symbol not found + return NULL; + } + + if ((DWORD)idx > exports->NumberOfFunctions) { + // name <-> ordinal number don't match + return NULL; + } + + // AddressOfFunctions contains the RVAs to the "real" functions + return (FARPROC) (module->remoteCodeBase + (*(DWORD *) (localCodeBase + exports->AddressOfFunctions + (idx*4)))); +} diff --git a/toolkit/crashreporter/LoadLibraryRemote.h b/toolkit/crashreporter/LoadLibraryRemote.h new file mode 100644 index 00000000000..343d8923efc --- /dev/null +++ b/toolkit/crashreporter/LoadLibraryRemote.h @@ -0,0 +1,24 @@ +/* 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 LoadLibraryRemote_h +#define LoadLibraryRemote_h + +#include + +/** + * Inject a library into a remote process. This injection has the following + * restrictions: + * + * - The DLL being injected must only depend on kernel32 and user32. + * - The entry point of the DLL is not run. If the DLL uses the CRT, it is + * the responsibility of the caller to make sure that _CRT_INIT is called. + * - There is no support for unloading a library once it has been loaded. + * - The symbol must be a named symbol and not an ordinal. + */ +void* LoadRemoteLibraryAndGetAddress(HANDLE hRemoteProcess, + const WCHAR* library, + const char* symbol); + +#endif // LoadLibraryRemote_h diff --git a/toolkit/crashreporter/Makefile.in b/toolkit/crashreporter/Makefile.in index b2a4c094351..e028dbd11b1 100644 --- a/toolkit/crashreporter/Makefile.in +++ b/toolkit/crashreporter/Makefile.in @@ -14,12 +14,10 @@ LIBXUL_LIBRARY = 1 LIBRARY_NAME = exception_handler_s ifeq ($(OS_ARCH),WINNT) -DIRS += \ - google-breakpad/src/common/windows \ - google-breakpad/src/client/windows/handler \ - google-breakpad/src/client/windows/sender \ - google-breakpad/src/client/windows/crash_generation \ - $(NULL) +DIRS += breakpad-windows-libxul +ifdef MOZ_CRASHREPORTER_INJECTOR +DIRS += breakpad-windows-standalone +endif endif ifeq ($(OS_ARCH),Darwin) @@ -74,6 +72,10 @@ endif DIRS += client +ifdef MOZ_CRASHREPORTER_INJECTOR +DIRS += injector +endif + LOCAL_INCLUDES = -I$(srcdir)/google-breakpad/src DEFINES += -DUNICODE -D_UNICODE @@ -85,6 +87,13 @@ CPPSRCS = \ nsExceptionHandler.cpp \ $(NULL) +ifdef MOZ_CRASHREPORTER_INJECTOR +CPPSRCS += \ + LoadLibraryRemote.cpp \ + InjectCrashReporter.cpp \ + $(NULL) +endif + FORCE_STATIC_LIB = 1 EXTRA_JS_MODULES = \ diff --git a/toolkit/crashreporter/breakpad-windows-libxul/Makefile.in b/toolkit/crashreporter/breakpad-windows-libxul/Makefile.in new file mode 100644 index 00000000000..46811b6e1fe --- /dev/null +++ b/toolkit/crashreporter/breakpad-windows-libxul/Makefile.in @@ -0,0 +1,41 @@ +# 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/. + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = google_breakpad_libxul_s +FORCE_STATIC_LIB = 1 + +STL_FLAGS = + +LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src + +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mk +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/handler/objs.mk +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/sender/objs.mk +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mk + +VPATH += \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/common/windows \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/handler \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/sender \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation \ + $(NULL) + +CPPSRCS = \ + $(objs_common) \ + $(objs_handler) \ + $(objs_sender) \ + $(objs_crash_generation) \ + http_upload.cc \ # required for the libxul version but not standalone + $(NULL) + +DEFINES += -DUNICODE -DUNICODE_ -DBREAKPAD_NO_TERMINATE_THREAD -DNOMINMAX + +include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/breakpad-windows-standalone/Makefile.in b/toolkit/crashreporter/breakpad-windows-standalone/Makefile.in new file mode 100644 index 00000000000..f17b759591f --- /dev/null +++ b/toolkit/crashreporter/breakpad-windows-standalone/Makefile.in @@ -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/. + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = google_breakpad_standalone_s +FORCE_STATIC_LIB = 1 +USE_STATIC_LIBS = 1 +MOZ_GLUE_LDFLAGS = + +STL_FLAGS = + +LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src + +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mk +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/handler/objs.mk +include $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mk + +VPATH += \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/common/windows \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/handler \ + $(topsrcdir)/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation \ + $(NULL) + +CPPSRCS = \ + $(objs_common) \ + $(objs_handler) \ + $(objs_crash_generation) \ + $(NULL) + +DEFINES += -DUNICODE -DUNICODE_ -DBREAKPAD_NO_TERMINATE_THREAD -DNOMINMAX + +include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/client/Makefile.in b/toolkit/crashreporter/client/Makefile.in index 8e0bb51f9a1..b2fad88ca85 100644 --- a/toolkit/crashreporter/client/Makefile.in +++ b/toolkit/crashreporter/client/Makefile.in @@ -37,8 +37,7 @@ CPPSRCS = \ ifeq ($(OS_ARCH),WINNT) CPPSRCS += crashreporter_win.cpp LIBS += \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/windows/sender/$(LIB_PREFIX)crash_report_sender_s.$(LIB_SUFFIX) \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/windows/$(LIB_PREFIX)breakpad_windows_common_s.$(LIB_SUFFIX) \ + $(DEPTH)/toolkit/crashreporter/breakpad-windows-libxul/$(LIB_PREFIX)google_breakpad_libxul_s.$(LIB_SUFFIX) $(NULL) LOCAL_INCLUDES += -I$(srcdir) RCINCLUDE = crashreporter.rc diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in deleted file mode 100644 index 6b9b5fe83d2..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in +++ /dev/null @@ -1,28 +0,0 @@ -# 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/. - -DEPTH = ../../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = crash_generation -LIBRARY_NAME = crash_generation_s - -LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src -DEFINES += -DUNICODE -D_UNICODE - -CPPSRCS = \ - client_info.cc \ - crash_generation_client.cc \ - crash_generation_server.cc \ - minidump_generator.cc \ - $(NULL) - -# need static lib -FORCE_STATIC_LIB = 1 - -include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.cc index 5e4e3cb9ee0..69f1ae0fba4 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.cc @@ -95,6 +95,27 @@ CrashGenerationClient::CrashGenerationClient( MINIDUMP_TYPE dump_type, const CustomClientInfo* custom_info) : pipe_name_(pipe_name), + pipe_handle_(NULL), + dump_type_(dump_type), + thread_id_(0), + server_process_id_(0), + crash_event_(NULL), + crash_generated_(NULL), + server_alive_(NULL), + exception_pointers_(NULL), + custom_info_() { + memset(&assert_info_, 0, sizeof(assert_info_)); + if (custom_info) { + custom_info_ = *custom_info; + } +} + +CrashGenerationClient::CrashGenerationClient( + HANDLE pipe_handle, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info) + : pipe_name_(), + pipe_handle_(pipe_handle), dump_type_(dump_type), thread_id_(0), server_process_id_(0), @@ -231,6 +252,12 @@ bool CrashGenerationClient::RegisterClient(HANDLE pipe) { HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, DWORD pipe_access, DWORD flags_attrs) { + if (pipe_handle_) { + HANDLE t = pipe_handle_; + pipe_handle_ = NULL; + return t; + } + for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { HANDLE pipe = CreateFile(pipe_name, pipe_access, @@ -325,4 +352,33 @@ bool CrashGenerationClient::SignalCrashEventAndWait() { return result == WAIT_OBJECT_0; } +HANDLE CrashGenerationClient::DuplicatePipeToClientProcess(const wchar_t* pipe_name, + HANDLE hProcess) { + for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { + HANDLE local_pipe = CreateFile(pipe_name, kPipeDesiredAccess, + 0, NULL, OPEN_EXISTING, + kPipeFlagsAndAttributes, NULL); + if (local_pipe != INVALID_HANDLE_VALUE) { + HANDLE remotePipe = INVALID_HANDLE_VALUE; + if (DuplicateHandle(GetCurrentProcess(), local_pipe, + hProcess, &remotePipe, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + return remotePipe; + } else { + return INVALID_HANDLE_VALUE; + } + } + + // Cannot continue retrying if the error wasn't a busy pipe. + if (GetLastError() != ERROR_PIPE_BUSY) { + return INVALID_HANDLE_VALUE; + } + + if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { + return INVALID_HANDLE_VALUE; + } + } + return INVALID_HANDLE_VALUE; +} + } // namespace google_breakpad diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.h b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.h index 01d13ddec58..bbef5a070f6 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.h +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_client.h @@ -66,6 +66,10 @@ class CrashGenerationClient { MINIDUMP_TYPE dump_type, const CustomClientInfo* custom_info); + CrashGenerationClient(HANDLE pipe_handle, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info); + ~CrashGenerationClient(); // Registers the client process with the crash server. @@ -92,6 +96,14 @@ class CrashGenerationClient { // false will be returned. bool RequestDump(MDRawAssertionInfo* assert_info); + // If the crash generation client is running in a sandbox that prevents it + // from opening the named pipe directly, the server process may open the + // handle and duplicate it into the client process with this helper method. + // Returns INVALID_HANDLE_VALUE on failure. The process must have been opened + // with the PROCESS_DUP_HANDLE access right. + static HANDLE DuplicatePipeToClientProcess(const wchar_t* pipe_name, + HANDLE hProcess); + private: // Connects to the appropriate pipe and sets the pipe handle state. // @@ -123,6 +135,10 @@ class CrashGenerationClient { // Pipe name to use to talk to server. std::wstring pipe_name_; + // Pipe handle duplicated from server process. Only valid before + // Register is called. + HANDLE pipe_handle_; + // Custom client information CustomClientInfo custom_info_; diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mk b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mk new file mode 100644 index 00000000000..7b82153ffb4 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/objs.mk @@ -0,0 +1,6 @@ +objs_crash_generation = \ + client_info.cc \ + crash_generation_client.cc \ + crash_generation_server.cc \ + minidump_generator.cc \ + $(NULL) diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in deleted file mode 100644 index 5e5613d7997..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in +++ /dev/null @@ -1,26 +0,0 @@ -# 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/. - -DEPTH = ../../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = handler -LIBRARY_NAME = exception_handler_s -XPI_NAME = crashreporter - -LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src -DEFINES += -DUNICODE -D_UNICODE -DBREAKPAD_NO_TERMINATE_THREAD -DNOMINMAX - -CPPSRCS = \ - exception_handler.cc \ - $(NULL) - -# need static lib -FORCE_STATIC_LIB = 1 - -include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc index deb46e022f0..8f4e3159894 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc @@ -156,9 +156,29 @@ ExceptionHandler::ExceptionHandler(const wstring& dump_path, handler_types, dump_type, pipe_name, + NULL, custom_info); } +ExceptionHandler::ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + HANDLE pipe_handle, + const CustomClientInfo* custom_info) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + dump_type, + NULL, + pipe_handle, + custom_info); +} + ExceptionHandler::ExceptionHandler(const wstring &dump_path, FilterCallback filter, MinidumpCallback callback, @@ -171,6 +191,7 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path, handler_types, MiniDumpNormal, NULL, + NULL, NULL); } @@ -181,6 +202,7 @@ void ExceptionHandler::Initialize(const wstring& dump_path, int handler_types, MINIDUMP_TYPE dump_type, const wchar_t* pipe_name, + HANDLE pipe_handle, const CustomClientInfo* custom_info) { LONG instance_count = InterlockedIncrement(&instance_count_); filter_ = filter; @@ -210,12 +232,22 @@ void ExceptionHandler::Initialize(const wstring& dump_path, handler_return_value_ = false; handle_debug_exceptions_ = false; - // Attempt to use out-of-process if user has specified pipe name. - if (pipe_name != NULL) { - scoped_ptr client( + // Attempt to use out-of-process if user has specified a pipe. + if (pipe_name != NULL || pipe_handle != NULL) { + assert(!(pipe_name && pipe_handle)); + + scoped_ptr client; + if (pipe_name) { + client.reset( new CrashGenerationClient(pipe_name, dump_type_, custom_info)); + } else { + client.reset( + new CrashGenerationClient(pipe_handle, + dump_type_, + custom_info)); + } // If successful in registering with the monitoring process, // there is no need to setup in-process crash generation. diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h index aa1eaf6f8e9..b49ada254ed 100644 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h @@ -164,7 +164,7 @@ class ExceptionHandler { void* callback_context, int handler_types); - // Creates a new ExcetpionHandler instance that can attempt to perform + // Creates a new ExceptionHandler instance that can attempt to perform // out-of-process dump generation if pipe_name is not NULL. If pipe_name is // NULL, or if out-of-process dump generation registration step fails, // in-process dump generation will be used. This also allows specifying @@ -178,6 +178,17 @@ class ExceptionHandler { const wchar_t* pipe_name, const CustomClientInfo* custom_info); + // As above, creates a new ExceptionHandler instance to perform + // out-of-process dump generation if the given pipe_handle is not NULL. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + HANDLE pipe_handle, + const CustomClientInfo* custom_info); + ~ExceptionHandler(); // Get and set the minidump path. @@ -253,6 +264,7 @@ class ExceptionHandler { int handler_types, MINIDUMP_TYPE dump_type, const wchar_t* pipe_name, + HANDLE pipe_handle, const CustomClientInfo* custom_info); // Function pointer type for MiniDumpWriteDump, which is looked up diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/objs.mk b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/objs.mk new file mode 100644 index 00000000000..a7624238320 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/objs.mk @@ -0,0 +1 @@ +objs_handler = exception_handler.cc diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/sender/Makefile.in b/toolkit/crashreporter/google-breakpad/src/client/windows/sender/Makefile.in deleted file mode 100644 index 33dfeda0272..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/client/windows/sender/Makefile.in +++ /dev/null @@ -1,26 +0,0 @@ -# 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/. - -DEPTH = ../../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = sender -LIBRARY_NAME = crash_report_sender_s - -LOCAL_INCLUDES = -I$(srcdir)/../../.. -DEFINES += -DUNICODE -D_UNICODE -STL_FLAGS = - -CPPSRCS = \ - crash_report_sender.cc \ - $(NULL) - -# need static lib -FORCE_STATIC_LIB = 1 - -include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/google-breakpad/src/client/windows/sender/objs.mk b/toolkit/crashreporter/google-breakpad/src/client/windows/sender/objs.mk new file mode 100644 index 00000000000..57cb6f6932b --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/client/windows/sender/objs.mk @@ -0,0 +1 @@ +objs_sender = crash_report_sender.cc diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/Makefile.in b/toolkit/crashreporter/google-breakpad/src/common/windows/Makefile.in deleted file mode 100644 index c2288fb7c8d..00000000000 --- a/toolkit/crashreporter/google-breakpad/src/common/windows/Makefile.in +++ /dev/null @@ -1,28 +0,0 @@ -# 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/. - -DEPTH = ../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = breakpad_windows_common -LIBRARY_NAME = breakpad_windows_common_s - -LOCAL_INCLUDES = -I$(srcdir)/../.. -DEFINES += -DUNICODE -D_UNICODE -STL_FLAGS = - -CPPSRCS = \ - guid_string.cc \ - http_upload.cc \ - string_utils.cc \ - $(NULL) - -# need static lib -FORCE_STATIC_LIB = 1 - -include $(topsrcdir)/config/rules.mk diff --git a/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mk b/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mk new file mode 100644 index 00000000000..2970dcff814 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mk @@ -0,0 +1,4 @@ +objs_common = \ + guid_string.cc \ + string_utils.cc \ + $(NULL) diff --git a/toolkit/crashreporter/injector/Makefile.in b/toolkit/crashreporter/injector/Makefile.in new file mode 100644 index 00000000000..ceef33e6ce5 --- /dev/null +++ b/toolkit/crashreporter/injector/Makefile.in @@ -0,0 +1,27 @@ +# 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/. + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = breakpadinjector +FORCE_SHARED_LIB = 1 +USE_STATIC_LIBS = 1 +STL_FLAGS = +MOZ_GLUE_LDFLAGS = + +CPPSRCS = injector.cpp + +SHARED_LIBRARY_LIBS += ../breakpad-windows-standalone/$(LIB_PREFIX)google_breakpad_standalone_s.$(LIB_SUFFIX) + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk +include $(topsrcdir)/config/rules.mk + +LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src +LDFLAGS += -ENTRY:DummyEntryPoint diff --git a/toolkit/crashreporter/injector/injector.cpp b/toolkit/crashreporter/injector/injector.cpp new file mode 100644 index 00000000000..4e922019e86 --- /dev/null +++ b/toolkit/crashreporter/injector/injector.cpp @@ -0,0 +1,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/. */ + +#include + +#include "client/windows/handler/exception_handler.h" + +using google_breakpad::ExceptionHandler; +using std::wstring; + +BOOL WINAPI DummyEntryPoint(HINSTANCE instance, + DWORD reason, + void* reserved) +{ + __debugbreak(); + + return FALSE; // We're being loaded remotely, this shouldn't happen! +} + +// support.microsoft.com/kb/94248 +extern "C" BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved); + +extern "C" +__declspec(dllexport) DWORD Start(void* context) +{ + // Because the remote DLL injector does not call DllMain, we have to + // initialize the CRT manually + _CRT_INIT(NULL, DLL_PROCESS_ATTACH, NULL); + + HANDLE hCrashPipe = reinterpret_cast(context); + + ExceptionHandler* e = new (std::nothrow) + ExceptionHandler(wstring(), NULL, NULL, NULL, + ExceptionHandler::HANDLER_ALL, + MiniDumpNormal, hCrashPipe, NULL); + if (e) + e->set_handle_debug_exceptions(true); + return 1; +} diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index 15324496332..067defe6e5b 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -62,6 +62,11 @@ #error "Not yet implemented for this platform" #endif // defined(XP_WIN32) +#ifdef MOZ_CRASHREPORTER_INJECTOR +#include "InjectCrashReporter.h" +using mozilla::InjectCrashRunnable; +#endif + #include #include #include @@ -219,6 +224,23 @@ static Mutex* dumpMapLock; typedef nsInterfaceHashtable ChildMinidumpMap; static ChildMinidumpMap* pidToMinidump; +#ifdef MOZ_CRASHREPORTER_INJECTOR +static nsIThread* sInjectorThread; +typedef nsDataHashtable InjectorPIDMap; +static InjectorPIDMap* pidToInjectorCallback; + +class ReportInjectedCrash : public nsRunnable +{ +public: + ReportInjectedCrash(PRUint32 pid) : mPID(pid) { } + + NS_IMETHOD Run(); + +private: + PRUint32 mPID; +}; +#endif // MOZ_CRASHREPORTER_INJECTOR + // Crashreporter annotations that we don't send along in subprocess // reports static const char* kSubprocessBlacklist[] = { @@ -808,7 +830,7 @@ nsresult SetExceptionHandler(nsIFile* aXREDirectory, #if defined(XP_WIN32) google_breakpad::ExceptionHandler::HANDLER_ALL, minidump_type, - NULL, + (const wchar_t*) NULL, NULL); #else true @@ -1911,8 +1933,13 @@ OnChildProcessDumpRequested(void* aContext, aClientInfo->pid(); #endif - MutexAutoLock lock(*dumpMapLock); - pidToMinidump->Put(pid, minidump); + { + MutexAutoLock lock(*dumpMapLock); + pidToMinidump->Put(pid, minidump); + } +#ifdef MOZ_CRASHREPORTER_INJECTOR + NS_DispatchToMainThread(new ReportInjectedCrash(pid)); +#endif } } @@ -2002,6 +2029,16 @@ OOPDeinit() return; } +#ifdef MOZ_CRASHREPORTER_INJECTOR + if (sInjectorThread) { + sInjectorThread->Shutdown(); + NS_RELEASE(sInjectorThread); + } + + delete pidToInjectorCallback; + pidToInjectorCallback = NULL; +#endif + delete crashServer; crashServer = NULL; @@ -2032,6 +2069,67 @@ GetChildNotificationPipe() } #endif +#ifdef MOZ_CRASHREPORTER_INJECTOR +void +InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb) +{ + if (!GetEnabled()) + return; + + if (!OOPInitialized()) + OOPInit(); + + if (!pidToInjectorCallback) { + pidToInjectorCallback = new InjectorPIDMap; + pidToInjectorCallback->Init(); + } + + if (!sInjectorThread) { + if (NS_FAILED(NS_NewThread(&sInjectorThread))) + return; + } + + pidToInjectorCallback->Put(processID, cb); + + nsCOMPtr r = new InjectCrashRunnable(processID); + sInjectorThread->Dispatch(r, nsIEventTarget::DISPATCH_NORMAL); +} + +NS_IMETHODIMP +ReportInjectedCrash::Run() +{ + // Crash reporting may have been disabled after this method was dispatched + if (!pidToInjectorCallback) + return NS_OK; + + InjectorCrashCallback* cb = pidToInjectorCallback->Get(mPID); + if (!cb) + return NS_OK; + + nsCOMPtr minidump; + if (!TakeMinidumpForChild(mPID, getter_AddRefs(minidump))) { + NS_WARNING("No minidump for crash notification."); + return NS_OK; + } + + nsString id; + GetIDFromMinidump(minidump, id); + + cb->OnCrash(mPID, id); + return NS_OK; +} + +void +UnregisterInjectorCallback(DWORD processID) +{ + if (!OOPInitialized()) + return; + + pidToInjectorCallback->Remove(processID); +} + +#endif // MOZ_CRASHREPORTER_INJECTOR + #if defined(XP_WIN) // Child-side API bool diff --git a/toolkit/crashreporter/nsExceptionHandler.h b/toolkit/crashreporter/nsExceptionHandler.h index 00a5b158802..e694cffcd97 100644 --- a/toolkit/crashreporter/nsExceptionHandler.h +++ b/toolkit/crashreporter/nsExceptionHandler.h @@ -111,6 +111,22 @@ bool CreatePairedMinidumps(ProcessHandle childPid, // Parent-side API for children const char* GetChildNotificationPipe(); +#ifdef MOZ_CRASHREPORTER_INJECTOR +// Inject a crash report client into an arbitrary process, and inform the +// callback object when it crashes. Parent process only. + +class InjectorCrashCallback +{ +public: + InjectorCrashCallback() { } + + virtual void OnCrash(DWORD processID, const nsAString& aDumpID) = 0; +}; + +void InjectCrashReporterIntoProcess(DWORD processID, InjectorCrashCallback* cb); +void UnregisterInjectorCallback(DWORD processID); +#endif + // Child-side API bool SetRemoteExceptionHandler(const nsACString& crashPipe); diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index 599eb21d629..15bcdb76110 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -1148,10 +1148,9 @@ if [ "$MOZ_CRASHREPORTER" ]; then " if [ "$OS_ARCH" = "WINNT" ]; then add_makefiles " - toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile - toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile - toolkit/crashreporter/google-breakpad/src/client/windows/sender/Makefile - toolkit/crashreporter/google-breakpad/src/common/windows/Makefile + toolkit/crashreporter/breakpad-windows-libxul/Makefile + toolkit/crashreporter/breakpad-windows-standalone/Makefile + toolkit/crashreporter/injector/Makefile " elif [ "$OS_ARCH" = "Darwin" ]; then add_makefiles " diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index da950662b75..91a8012319d 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -128,9 +128,7 @@ ifdef MOZ_CRASHREPORTER SHARED_LIBRARY_LIBS += $(DEPTH)/toolkit/crashreporter/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) ifeq ($(OS_ARCH),WINNT) SHARED_LIBRARY_LIBS += \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/windows/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/$(LIB_PREFIX)crash_generation_s.$(LIB_SUFFIX) \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/windows/$(LIB_PREFIX)breakpad_windows_common_s.$(LIB_SUFFIX) + $(DEPTH)/toolkit/crashreporter/breakpad-windows-libxul/$(LIB_PREFIX)google_breakpad_libxul_s.$(LIB_SUFFIX) endif ifeq ($(OS_ARCH),Darwin)