Bug 1354077: Annotate crash reports with COM interface configuration information when marshaling fails; r=jimm

MozReview-Commit-ID: GCYLbtu1Nlb

--HG--
extra : rebase_source : 565c598dcba929cb1b774b3440180c60e78da9cd
extra : amend_source : dc0051579e0b005c89fd3835f0c2b0d4fd87a280
This commit is contained in:
Aaron Klotz 2017-05-04 15:41:35 -06:00
Родитель c220978a34
Коммит d82e0194e8
7 изменённых файлов: 439 добавлений и 13 удалений

Просмотреть файл

@ -0,0 +1,362 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "InterfaceRegistrationAnnotator.h"
#include "mozilla/JSONWriter.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/NotNull.h"
#include "nsExceptionHandler.h"
#include "nsWindowsHelpers.h"
#include <oleauto.h>
namespace {
class CStringWriter final : public mozilla::JSONWriteFunc
{
public:
void Write(const char* aStr) override
{
mBuf += aStr;
}
const nsCString& Get() const
{
return mBuf;
}
private:
nsCString mBuf;
};
} // anonymous namespace
namespace mozilla {
namespace mscom {
static const char16_t kSoftwareClasses[] = u"SOFTWARE\\Classes";
static const char16_t kInterface[] = u"\\Interface\\";
static const char16_t kDefaultValue[] = u"";
static const char16_t kThreadingModel[] = u"ThreadingModel";
static const char16_t kBackslash[] = u"\\";
static const char16_t kFlags[] = u"FLAGS";
static const char16_t kProxyStubClsid32[] = u"\\ProxyStubClsid32";
static const char16_t kClsid[] = u"\\CLSID\\";
static const char16_t kInprocServer32[] = u"\\InprocServer32";
static const char16_t kTypeLib[] = u"\\TypeLib";
static const char16_t kVersion[] = u"Version";
static const char16_t kWin32[] = u"Win32";
static const char16_t kWin64[] = u"Win64";
template <size_t N>
inline static bool
GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
const char16_t (&aValueName)[N], nsAString& aOutput)
{
return GetStringValue(aBaseKey, aStrSubKey,
nsLiteralString(aValueName),
aOutput);
}
static bool
GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
const nsAString& aValueName, nsAString& aOutput)
{
const nsString& flatSubKey = PromiseFlatString(aStrSubKey);
const nsString& flatValueName = PromiseFlatString(aValueName);
LPCWSTR valueName = aValueName.IsEmpty() ? nullptr : flatValueName.get();
DWORD numBytes = 0;
LONG result = RegGetValue(aBaseKey, flatSubKey.get(), valueName,
RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ, nullptr,
nullptr, &numBytes);
if (result != ERROR_SUCCESS) {
return false;
}
int numChars = (numBytes + 1) / sizeof(wchar_t);
aOutput.SetLength(numChars);
result = RegGetValue(aBaseKey, flatSubKey.get(), valueName,
RRF_RT_REG_SZ | RRF_RT_REG_EXPAND_SZ,
nullptr, aOutput.BeginWriting(), &numBytes);
if (result == ERROR_SUCCESS) {
// Truncate null terminator
aOutput.SetLength(((numBytes + 1) / sizeof(wchar_t)) - 1);
}
return result == ERROR_SUCCESS;
}
/**
* This function fails unless the entire string has been converted.
* (eg, the string "FLAGS" will convert to 0xF but we will return false)
*/
static bool
ConvertLCID(const wchar_t* aStr, NotNull<unsigned long*> aOutLcid)
{
wchar_t* endChar;
*aOutLcid = wcstoul(aStr, &endChar, 16);
return *endChar == 0;
}
static bool
GetLoadedPath(nsAString& aPath)
{
// These paths may be REG_EXPAND_SZ, so we expand any environment strings
DWORD bufCharLen = ExpandEnvironmentStrings(PromiseFlatString(aPath).get(),
nullptr, 0);
auto buf = MakeUnique<WCHAR[]>(bufCharLen);
if (!ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), buf.get(),
bufCharLen)) {
return false;
}
// Use LoadLibrary so that the DLL is resolved using the loader's DLL search
// rules
nsModuleHandle mod(LoadLibrary(buf.get()));
if (!mod) {
return false;
}
WCHAR finalPath[MAX_PATH + 1] = {};
DWORD result = GetModuleFileNameW(mod, finalPath, ArrayLength(finalPath));
if (!result || (result == ArrayLength(finalPath) &&
GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
return false;
}
aPath = nsDependentString(finalPath, result);
return true;
}
static void
AnnotateClsidRegistrationForHive(JSONWriter& aJson, HKEY aHive,
const nsAString& aClsid,
const JSONWriter::CollectionStyle aStyle)
{
nsAutoString clsidSubkey;
clsidSubkey.AppendLiteral(kSoftwareClasses);
clsidSubkey.AppendLiteral(kClsid);
clsidSubkey.Append(aClsid);
nsAutoString className;
if (GetStringValue(aHive, clsidSubkey, kDefaultValue, className)) {
aJson.StringProperty("ClassName",
NS_ConvertUTF16toUTF8(className).get());
}
nsAutoString inprocServerSubkey(clsidSubkey);
inprocServerSubkey.AppendLiteral(kInprocServer32);
nsAutoString pathToServerDll;
if (GetStringValue(aHive, inprocServerSubkey, kDefaultValue, pathToServerDll)) {
aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(pathToServerDll).get());
if (GetLoadedPath(pathToServerDll)) {
aJson.StringProperty("LoadedPath",
NS_ConvertUTF16toUTF8(pathToServerDll).get());
}
}
nsAutoString apartment;
if (GetStringValue(aHive, inprocServerSubkey, kThreadingModel, apartment)) {
aJson.StringProperty("ThreadingModel", NS_ConvertUTF16toUTF8(apartment).get());
}
}
static void
CheckTlbPath(JSONWriter& aJson, const nsAString& aTypelibPath)
{
const nsString& flatPath = PromiseFlatString(aTypelibPath);
DWORD bufCharLen = ExpandEnvironmentStrings(flatPath.get(), nullptr, 0);
auto buf = MakeUnique<WCHAR[]>(bufCharLen);
if (!ExpandEnvironmentStrings(flatPath.get(), buf.get(), bufCharLen)) {
return;
}
// See whether this tlb can actually be loaded
RefPtr<ITypeLib> typeLib;
HRESULT hr = LoadTypeLibEx(buf.get(), REGKIND_NONE, getter_AddRefs(typeLib));
nsPrintfCString loadResult("0x%08X", hr);
aJson.StringProperty("LoadResult", loadResult.get());
}
template <size_t N>
static void
AnnotateTypelibPlatform(JSONWriter& aJson, HKEY aBaseKey,
const nsAString& aLcidSubkey,
const char16_t (&aPlatform)[N],
const JSONWriter::CollectionStyle aStyle)
{
nsLiteralString platform(aPlatform);
nsAutoString fullSubkey(aLcidSubkey);
fullSubkey.AppendLiteral(kBackslash);
fullSubkey.Append(platform);
nsAutoString tlbPath;
if (GetStringValue(aBaseKey, fullSubkey, kDefaultValue, tlbPath)) {
aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(platform).get(), aStyle);
aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(tlbPath).get());
CheckTlbPath(aJson, tlbPath);
aJson.EndObject();
}
}
static void
AnnotateTypelibRegistrationForHive(JSONWriter& aJson, HKEY aHive,
const nsAString& aTypelibId,
const nsAString& aTypelibVersion,
const JSONWriter::CollectionStyle aStyle)
{
nsAutoString typelibSubKey;
typelibSubKey.AppendLiteral(kSoftwareClasses);
typelibSubKey.AppendLiteral(kTypeLib);
typelibSubKey.AppendLiteral(kBackslash);
typelibSubKey.Append(aTypelibId);
typelibSubKey.AppendLiteral(kBackslash);
typelibSubKey.Append(aTypelibVersion);
nsAutoString typelibDesc;
if (GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibDesc)) {
aJson.StringProperty("Description",
NS_ConvertUTF16toUTF8(typelibDesc).get());
}
nsAutoString flagsSubKey(typelibSubKey);
flagsSubKey.AppendLiteral(kBackslash);
flagsSubKey.AppendLiteral(kFlags);
nsAutoString typelibFlags;
if (GetStringValue(aHive, flagsSubKey, kDefaultValue, typelibFlags)) {
aJson.StringProperty("Flags", NS_ConvertUTF16toUTF8(typelibFlags).get());
}
HKEY rawTypelibKey;
LONG result = RegOpenKeyEx(aHive, typelibSubKey.get(), 0, KEY_READ,
&rawTypelibKey);
if (result != ERROR_SUCCESS) {
return;
}
nsAutoRegKey typelibKey(rawTypelibKey);
const size_t kMaxLcidCharLen = 9;
WCHAR keyName[kMaxLcidCharLen];
for (DWORD index = 0; result == ERROR_SUCCESS; ++index) {
DWORD keyNameLength = ArrayLength(keyName);
result = RegEnumKeyEx(typelibKey, index, keyName, &keyNameLength, nullptr,
nullptr, nullptr, nullptr);
unsigned long lcid;
if (result == ERROR_SUCCESS && ConvertLCID(keyName, WrapNotNull(&lcid))) {
nsDependentString strLcid(keyName, keyNameLength);
aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(strLcid).get(), aStyle);
AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin32, aStyle);
#if defined(HAVE_64BIT_BUILD)
AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin64, aStyle);
#endif
aJson.EndObject();
}
}
}
static void
AnnotateInterfaceRegistrationForHive(JSONWriter& aJson, HKEY aHive, REFIID aIid,
const JSONWriter::CollectionStyle aStyle)
{
nsAutoString interfaceSubKey;
interfaceSubKey.AppendLiteral(kSoftwareClasses);
interfaceSubKey.AppendLiteral(kInterface);
nsAutoString iid;
GUIDToString(aIid, iid);
interfaceSubKey.Append(iid);
nsAutoString interfaceName;
if (GetStringValue(aHive, interfaceSubKey, kDefaultValue, interfaceName)) {
aJson.StringProperty("InterfaceName",
NS_ConvertUTF16toUTF8(interfaceName).get());
}
nsAutoString psSubKey(interfaceSubKey);
psSubKey.AppendLiteral(kProxyStubClsid32);
nsAutoString psClsid;
if (GetStringValue(aHive, psSubKey, kDefaultValue, psClsid)) {
aJson.StartObjectProperty("ProxyStub", aStyle);
aJson.StringProperty("CLSID", NS_ConvertUTF16toUTF8(psClsid).get());
AnnotateClsidRegistrationForHive(aJson, aHive, psClsid, aStyle);
aJson.EndObject();
}
nsAutoString typelibSubKey(interfaceSubKey);
typelibSubKey.AppendLiteral(kTypeLib);
nsAutoString typelibId;
bool haveTypelibId = GetStringValue(aHive, typelibSubKey, kDefaultValue,
typelibId);
nsAutoString typelibVersion;
bool haveTypelibVersion = GetStringValue(aHive, typelibSubKey, kVersion,
typelibVersion);
if (haveTypelibId || haveTypelibVersion) {
aJson.StartObjectProperty("TypeLib", aStyle);
}
if (haveTypelibId) {
aJson.StringProperty("ID", NS_ConvertUTF16toUTF8(typelibId).get());
}
if (haveTypelibVersion) {
aJson.StringProperty("Version", NS_ConvertUTF16toUTF8(typelibVersion).get());
}
if (haveTypelibId && haveTypelibVersion) {
AnnotateTypelibRegistrationForHive(aJson, aHive, typelibId, typelibVersion,
aStyle);
}
if (haveTypelibId || haveTypelibVersion) {
aJson.EndObject();
}
}
void
AnnotateInterfaceRegistration(REFIID aIid)
{
#if defined(DEBUG)
const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
#else
const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
#endif
JSONWriter json(MakeUnique<CStringWriter>());
json.Start(style);
json.StartObjectProperty("HKLM", style);
AnnotateInterfaceRegistrationForHive(json, HKEY_LOCAL_MACHINE, aIid, style);
json.EndObject();
json.StartObjectProperty("HKCU", style);
AnnotateInterfaceRegistrationForHive(json, HKEY_CURRENT_USER, aIid, style);
json.EndObject();
json.End();
CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("InterfaceRegistrationInfo"),
static_cast<CStringWriter*>(json.WriteFunc())->Get());
}
} // namespace mscom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,22 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 mozilla_mscom_InterfaceRegistrationAnnotator_h
#define mozilla_mscom_InterfaceRegistrationAnnotator_h
#if !defined(MOZ_CRASHREPORTER)
#error "This header requires crash reporting to be enabled"
#endif
namespace mozilla {
namespace mscom {
void AnnotateInterfaceRegistration(REFIID aIid);
} // namespace mscom
} // namespace mozilla
#endif // mozilla_mscom_InterfaceRegistrationAnnotator_h

Просмотреть файл

@ -10,7 +10,8 @@
#include "mozilla/mscom/Utils.h"
#include "mozilla/WindowsVersion.h"
#ifdef MOZ_CRASHREPORTER
#if defined(MOZ_CRASHREPORTER)
#include "InterfaceRegistrationAnnotator.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
#endif
@ -36,6 +37,7 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
, mGlobalLockedBuf(nullptr)
, mHGlobal(nullptr)
, mBufSize(aInitBufSize)
, mUnmarshalResult(E_UNEXPECTED)
{
if (!aInitBufSize) {
// We marshaled a nullptr. Nothing else to do here.
@ -49,8 +51,6 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
return;
}
HRESULT unmarshalResult = S_OK;
// We need to convert to an interface here otherwise we mess up const
// correctness with IPDL. We'll request an IUnknown and then QI the
// actual interface later.
@ -59,10 +59,10 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
{
// OK to forget mStream when calling into this function because the stream
// gets released even if the unmarshaling part fails.
unmarshalResult =
mUnmarshalResult =
::CoGetInterfaceAndReleaseStream(mStream.forget().take(), IID_IUnknown,
getter_AddRefs(mUnmarshaledProxy));
MOZ_ASSERT(SUCCEEDED(unmarshalResult));
MOZ_ASSERT(SUCCEEDED(mUnmarshalResult));
};
if (XRE_IsParentProcess()) {
@ -75,8 +75,8 @@ ProxyStream::ProxyStream(const BYTE* aInitBuf, const int aInitBufSize)
}
#if defined(MOZ_CRASHREPORTER)
if (FAILED(unmarshalResult)) {
nsPrintfCString hrAsStr("0x%08X", unmarshalResult);
if (FAILED(mUnmarshalResult)) {
nsPrintfCString hrAsStr("0x%08X", mUnmarshalResult);
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("CoGetInterfaceAndReleaseStreamFailure"), hrAsStr);
}
@ -197,6 +197,12 @@ ProxyStream::GetInterface(REFIID aIID, void** aOutInterface) const
return false;
}
#if defined(MOZ_CRASHREPORTER)
if (FAILED(mUnmarshalResult)) {
AnnotateInterfaceRegistration(aIID);
}
#endif
if (!mUnmarshaledProxy) {
*aOutInterface = nullptr;
return true;
@ -269,6 +275,7 @@ ProxyStream::ProxyStream(REFIID aIID, IUnknown* aObject)
#if defined(MOZ_CRASHREPORTER)
if (FAILED(marshalResult)) {
AnnotateInterfaceRegistration(aIID);
nsPrintfCString hrAsStr("0x%08X", marshalResult);
CrashReporter::AnnotateCrashReport(
NS_LITERAL_CSTRING("CoMarshalInterfaceFailure"), hrAsStr);

Просмотреть файл

@ -55,6 +55,7 @@ private:
HGLOBAL mHGlobal;
int mBufSize;
ProxyUniquePtr<IUnknown> mUnmarshaledProxy;
HRESULT mUnmarshalResult;
};
} // namespace mscom

Просмотреть файл

@ -74,7 +74,27 @@ IsValidGUID(REFGUID aCheckGuid)
return version == 1 || version == 4;
}
#ifdef ACCESSIBILITY
#if defined(MOZILLA_INTERNAL_API)
void
GUIDToString(REFGUID aGuid, nsAString& aOutString)
{
// This buffer length is long enough to hold a GUID string that is formatted
// to include curly braces and dashes.
const int kBufLenWithNul = 39;
aOutString.SetLength(kBufLenWithNul);
int result = StringFromGUID2(aGuid, wwc(aOutString.BeginWriting()), kBufLenWithNul);
MOZ_ASSERT(result);
if (result) {
// Truncate the terminator
aOutString.SetLength(result - 1);
}
}
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
static bool
IsVtableIndexFromParentInterface(TYPEATTR* aTypeAttr,
unsigned long aVtableIndex)
@ -202,7 +222,8 @@ IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
return false;
}
#endif // ifdef ACCESSIBILITY
#endif // defined(ACCESSIBILITY)
} // namespace mscom
} // namespace mozilla

Просмотреть файл

@ -7,9 +7,13 @@
#ifndef mozilla_mscom_Utils_h
#define mozilla_mscom_Utils_h
#ifdef ACCESSIBILITY
#if defined(MOZILLA_INTERNAL_API)
#include "nsString.h"
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
#include <guiddef.h>
#endif
#endif // defined(ACCESSIBILITY)
struct IUnknown;
@ -20,12 +24,16 @@ bool IsCurrentThreadMTA();
bool IsProxy(IUnknown* aUnknown);
bool IsValidGUID(REFGUID aCheckGuid);
#ifdef ACCESSIBILITY
#if defined(MOZILLA_INTERNAL_API)
void GUIDToString(REFGUID aGuid, nsAString& aOutString);
#endif // defined(MOZILLA_INTERNAL_API)
#if defined(ACCESSIBILITY)
bool IsVtableIndexFromParentInterface(REFIID aInterface,
unsigned long aVtableIndex);
bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
unsigned long aVtableIndexHint);
#endif
#endif // defined(ACCESSIBILITY)
} // namespace mscom
} // namespace mozilla

Просмотреть файл

@ -25,6 +25,11 @@ UNIFIED_SOURCES += [
'Utils.cpp',
]
if CONFIG['MOZ_CRASHREPORTER']:
UNIFIED_SOURCES += [
'InterfaceRegistrationAnnotator.cpp',
]
if CONFIG['ACCESSIBILITY']:
DIRS += [
'oop',