# HG changeset patch # User Aaron Klotz # Date 1518563041 25200 # Node ID 341f20ef2627e0b19a324de14f9b32ed743bda92 # Parent b33a80b6d4c447fa0a0e7b46ecef7b8fe09e3757 Bug 1436845: Part 3 - Add support for ModuleSignatureInfo field in .extra file on Windows builds; r=ted We want to send information about the organization whose cert was used to sign modules in our address space. Originally I had written this code to accumulate that info within Firefox, but I realized that a better option is to do this from the minidump analyzer: (1) This way does not affect browser performance; (2) This way allows us to properly gather cert info even from startup crashes. This functionality was reviewed for data collection via bug 1430857. diff --git a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp --- a/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp +++ b/toolkit/crashreporter/minidump-analyzer/minidump-analyzer.cpp @@ -21,16 +21,17 @@ #include "google_breakpad/processor/stack_frame.h" #include "processor/pathname_stripper.h" #include "mozilla/FStream.h" #if defined(XP_WIN32) #include +#include "mozilla/glue/WindowsDllServices.h" #elif defined(XP_UNIX) || defined(XP_MACOSX) #include #include #include #endif @@ -38,16 +39,22 @@ #include "MinidumpAnalyzerUtils.h" #if XP_WIN && HAVE_64BIT_BUILD #include "MozStackFrameSymbolizer.h" #endif namespace CrashReporter { +#if defined(XP_WIN) + +static mozilla::glue::BasicDllServices gDllServices; + +#endif + using std::ios; using std::ios_base; using std::hex; using std::map; using std::showbase; using std::string; using std::stringstream; using std::wstring; @@ -174,17 +181,17 @@ ConvertStackToJSON(const ProcessState& a } // Convert the list of modules to JSON and append them to the array specified // in the |aNode| parameter. static int ConvertModulesToJSON(const ProcessState& aProcessState, OrderedModulesMap& aOrderedModules, - Json::Value& aNode) + Json::Value& aNode, Json::Value& aCertSubjects) { const CodeModules* modules = aProcessState.modules(); if (!modules) { return -1; } // Create a sorted set of modules so that we'll be able to lookup the index @@ -212,16 +219,34 @@ ConvertModulesToJSON(const ProcessState& ++moduleSequence) { const CodeModule *module = modules->GetModuleAtSequence(moduleSequence); if (module->base_address() == mainAddress) { mainModuleIndex = moduleSequence; } +#if defined(XP_WIN) + auto certSubject = gDllServices.GetBinaryOrgName( + UTF8ToWide(module->code_file()).c_str()); + if (certSubject) { + string strSubject(WideToUTF8(certSubject.get())); + // Json::Value::operator[] creates and returns a null member if the key + // does not exist. + Json::Value& subjectNode = aCertSubjects[strSubject]; + if (!subjectNode) { + // If the member is null, we want to convert that to an array. + subjectNode = Json::Value(Json::arrayValue); + } + + // Now we're guaranteed that subjectNode is an array. Add the new entry. + subjectNode.append(PathnameStripper::File(module->code_file())); + } +#endif + Json::Value moduleNode; moduleNode["filename"] = PathnameStripper::File(module->code_file()); moduleNode["code_id"] = PathnameStripper::File(module->code_identifier()); moduleNode["version"] = module->version(); moduleNode["debug_file"] = PathnameStripper::File(module->debug_file()); moduleNode["debug_id"] = module->debug_identifier(); moduleNode["base_addr"] = ToHex(module->base_address()); moduleNode["end_addr"] = ToHex(module->base_address() + module->size()); @@ -231,18 +256,20 @@ ConvertModulesToJSON(const ProcessState& return mainModuleIndex; } // Convert the process state to JSON, this includes information about the // crash, the module list and stack traces for every thread static void -ConvertProcessStateToJSON(const ProcessState& aProcessState, Json::Value& aRoot, - const bool aFullStacks) +ConvertProcessStateToJSON(const ProcessState& aProcessState, + Json::Value& aStackTraces, + const bool aFullStacks, + Json::Value& aCertSubjects) { // We use this map to get the index of a module when listed by address OrderedModulesMap orderedModules; // Crash info Json::Value crashInfo; int requestingThread = aProcessState.requesting_thread(); @@ -261,27 +288,28 @@ ConvertProcessStateToJSON(const ProcessS // Add assertion info, if available string assertion = aProcessState.assertion(); if (!assertion.empty()) { crashInfo["assertion"] = assertion; } } - aRoot["crash_info"] = crashInfo; + aStackTraces["crash_info"] = crashInfo; // Modules Json::Value modules(Json::arrayValue); - int mainModule = ConvertModulesToJSON(aProcessState, orderedModules, modules); + int mainModule = ConvertModulesToJSON(aProcessState, orderedModules, + modules, aCertSubjects); if (mainModule != -1) { - aRoot["main_module"] = mainModule; + aStackTraces["main_module"] = mainModule; } - aRoot["modules"] = modules; + aStackTraces["modules"] = modules; // Threads Json::Value threads(Json::arrayValue); int threadCount = aProcessState.threads()->size(); if (!aFullStacks && (requestingThread != -1)) { // Only add the crashing thread Json::Value thread; @@ -298,24 +326,27 @@ ConvertProcessStateToJSON(const ProcessS const CallStack* rawStack = aProcessState.threads()->at(threadIndex); ConvertStackToJSON(aProcessState, orderedModules, rawStack, stack); thread["frames"] = stack; threads.append(thread); } } - aRoot["threads"] = threads; + aStackTraces["threads"] = threads; } // Process the minidump file and append the JSON-formatted stack traces to -// the node specified in |aRoot| - +// the node specified in |aStackTraces|. We also populate |aCertSubjects| with +// information about the certificates used to sign modules, when present and +// supported by the underlying OS. static bool -ProcessMinidump(Json::Value& aRoot, const string& aDumpFile, const bool aFullStacks) { +ProcessMinidump(Json::Value& aStackTraces, Json::Value& aCertSubjects, + const string& aDumpFile, const bool aFullStacks) +{ #if XP_WIN && HAVE_64BIT_BUILD MozStackFrameSymbolizer symbolizer; MinidumpProcessor minidumpProcessor(&symbolizer, false); #else BasicSourceLineResolver resolver; // We don't have a valid symbol resolver so we pass nullptr instead. MinidumpProcessor minidumpProcessor(nullptr, &resolver); #endif @@ -329,28 +360,29 @@ ProcessMinidump(Json::Value& aRoot, cons #endif // defined(XP_WIN) if (!dump.Read()) { return false; } ProcessResult rv; ProcessState processState; rv = minidumpProcessor.Process(&dump, &processState); - aRoot["status"] = ResultString(rv); + aStackTraces["status"] = ResultString(rv); - ConvertProcessStateToJSON(processState, aRoot, aFullStacks); + ConvertProcessStateToJSON(processState, aStackTraces, aFullStacks, + aCertSubjects); return true; } -// Update the extra data file by adding the StackTraces field holding the -// JSON output of this program. - +// Update the extra data file by adding the StackTraces and ModuleSignatureInfo +// fields that contain the JSON outputs of this program. static bool -UpdateExtraDataFile(const string &aDumpPath, const Json::Value& aRoot) +UpdateExtraDataFile(const string &aDumpPath, const Json::Value& aStackTraces, + const Json::Value& aCertSubjects) { string extraDataPath(aDumpPath); int dot = extraDataPath.rfind('.'); if (dot < 0) { return false; // Not a valid dump path } @@ -365,34 +397,40 @@ UpdateExtraDataFile(const string &aDumpP #else extraDataPath.c_str(), #endif // defined(XP_WIN) mode); if (f.is_open()) { Json::FastWriter writer; - f << "StackTraces=" << writer.write(aRoot); + f << "StackTraces=" << writer.write(aStackTraces); res = !f.fail(); + if (!!aCertSubjects) { + f << "ModuleSignatureInfo=" << writer.write(aCertSubjects); + res &= !f.fail(); + } + f.close(); } return res; } bool GenerateStacks(const string& aDumpPath, const bool aFullStacks) { - Json::Value root; + Json::Value stackTraces; + Json::Value certSubjects; - if (!ProcessMinidump(root, aDumpPath, aFullStacks)) { + if (!ProcessMinidump(stackTraces, certSubjects, aDumpPath, aFullStacks)) { return false; } - return UpdateExtraDataFile(aDumpPath, root); + return UpdateExtraDataFile(aDumpPath, stackTraces, certSubjects); } } // namespace CrashReporter using namespace CrashReporter; #if defined(XP_WIN) #define XP_LITERAL(s) L##s