1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
|
/* -*- 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 "RegistrationAnnotator.h"
#include "mozilla/JSONWriter.h"
#include "mozilla/mscom/Utils.h"
#include "mozilla/NotNull.h"
#include "nsExceptionHandler.h"
#include "nsPrintfCString.h"
#include "nsWindowsHelpers.h"
#include "nsXULAppAPI.h"
#include <oleauto.h>
namespace {
class CStringWriter final : public mozilla::JSONWriteFunc {
public:
void Write(const mozilla::Span<const char>& aStr) override {
mBuf.Append(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 kInprocHandler32[] = u"\\InprocHandler32";
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";
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 type = 0;
DWORD numBytes = 0;
LONG result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, RRF_RT_ANY,
&type, nullptr, &numBytes);
if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
return false;
}
int numChars = (numBytes + 1) / sizeof(wchar_t);
aOutput.SetLength(numChars);
DWORD acceptFlag = type == REG_SZ ? RRF_RT_REG_SZ : RRF_RT_REG_EXPAND_SZ;
result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, acceptFlag,
nullptr, aOutput.BeginWriting(), &numBytes);
if (result == ERROR_SUCCESS) {
// Truncate null terminator
aOutput.SetLength(((numBytes + 1) / sizeof(wchar_t)) - 1);
}
return result == ERROR_SUCCESS;
}
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);
}
/**
* 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));
}
nsAutoString inprocServerSubkey(clsidSubkey);
inprocServerSubkey.AppendLiteral(kInprocServer32);
nsAutoString pathToServerDll;
if (GetStringValue(aHive, inprocServerSubkey, kDefaultValue,
pathToServerDll)) {
aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(pathToServerDll));
if (GetLoadedPath(pathToServerDll)) {
aJson.StringProperty("LoadedPath",
NS_ConvertUTF16toUTF8(pathToServerDll));
}
}
nsAutoString apartment;
if (GetStringValue(aHive, inprocServerSubkey, kThreadingModel, apartment)) {
aJson.StringProperty("ThreadingModel", NS_ConvertUTF16toUTF8(apartment));
}
nsAutoString inprocHandlerSubkey(clsidSubkey);
inprocHandlerSubkey.AppendLiteral(kInprocHandler32);
nsAutoString pathToHandlerDll;
if (GetStringValue(aHive, inprocHandlerSubkey, kDefaultValue,
pathToHandlerDll)) {
aJson.StringProperty("HandlerPath",
NS_ConvertUTF16toUTF8(pathToHandlerDll));
if (GetLoadedPath(pathToHandlerDll)) {
aJson.StringProperty("LoadedHandlerPath",
NS_ConvertUTF16toUTF8(pathToHandlerDll));
}
}
nsAutoString handlerApartment;
if (GetStringValue(aHive, inprocHandlerSubkey, kThreadingModel,
handlerApartment)) {
aJson.StringProperty("HandlerThreadingModel",
NS_ConvertUTF16toUTF8(handlerApartment));
}
}
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);
}
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), aStyle);
aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(tlbPath));
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));
}
nsAutoString flagsSubKey(typelibSubKey);
flagsSubKey.AppendLiteral(kBackslash);
flagsSubKey.AppendLiteral(kFlags);
nsAutoString typelibFlags;
if (GetStringValue(aHive, flagsSubKey, kDefaultValue, typelibFlags)) {
aJson.StringProperty("Flags", NS_ConvertUTF16toUTF8(typelibFlags));
}
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), 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));
}
nsAutoString psSubKey(interfaceSubKey);
psSubKey.AppendLiteral(kProxyStubClsid32);
nsAutoString psClsid;
if (GetStringValue(aHive, psSubKey, kDefaultValue, psClsid)) {
aJson.StartObjectProperty("ProxyStub", aStyle);
aJson.StringProperty("CLSID", NS_ConvertUTF16toUTF8(psClsid));
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));
}
if (haveTypelibVersion) {
aJson.StringProperty("Version", NS_ConvertUTF16toUTF8(typelibVersion));
}
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::Annotation annotationKey;
if (XRE_IsParentProcess()) {
annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoParent;
} else {
annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoChild;
}
CrashReporter::AnnotateCrashReport(
annotationKey, static_cast<CStringWriter*>(json.WriteFunc())->Get());
}
void AnnotateClassRegistration(REFCLSID aClsid) {
#if defined(DEBUG)
const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
#else
const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
#endif
nsAutoString strClsid;
GUIDToString(aClsid, strClsid);
JSONWriter json(MakeUnique<CStringWriter>());
json.Start(style);
json.StartObjectProperty("HKLM", style);
AnnotateClsidRegistrationForHive(json, HKEY_LOCAL_MACHINE, strClsid, style);
json.EndObject();
json.StartObjectProperty("HKCU", style);
AnnotateClsidRegistrationForHive(json, HKEY_CURRENT_USER, strClsid, style);
json.EndObject();
json.End();
CrashReporter::Annotation annotationKey;
if (XRE_IsParentProcess()) {
annotationKey = CrashReporter::Annotation::ClassRegistrationInfoParent;
} else {
annotationKey = CrashReporter::Annotation::ClassRegistrationInfoChild;
}
CrashReporter::AnnotateCrashReport(
annotationKey, static_cast<CStringWriter*>(json.WriteFunc())->Get());
}
} // namespace mscom
} // namespace mozilla
|