1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "RegistrationAnnotator.h"
8
9 #include "mozilla/JSONWriter.h"
10 #include "mozilla/mscom/Utils.h"
11 #include "mozilla/NotNull.h"
12 #include "nsExceptionHandler.h"
13 #include "nsPrintfCString.h"
14 #include "nsWindowsHelpers.h"
15 #include "nsXULAppAPI.h"
16
17 #include <oleauto.h>
18
19 namespace {
20
21 class CStringWriter final : public mozilla::JSONWriteFunc {
22 public:
Write(const mozilla::Span<const char> & aStr)23 void Write(const mozilla::Span<const char>& aStr) override {
24 mBuf.Append(aStr);
25 }
26
Get() const27 const nsCString& Get() const { return mBuf; }
28
29 private:
30 nsCString mBuf;
31 };
32
33 } // anonymous namespace
34
35 namespace mozilla {
36 namespace mscom {
37
38 static const char16_t kSoftwareClasses[] = u"SOFTWARE\\Classes";
39 static const char16_t kInterface[] = u"\\Interface\\";
40 static const char16_t kDefaultValue[] = u"";
41 static const char16_t kThreadingModel[] = u"ThreadingModel";
42 static const char16_t kBackslash[] = u"\\";
43 static const char16_t kFlags[] = u"FLAGS";
44 static const char16_t kProxyStubClsid32[] = u"\\ProxyStubClsid32";
45 static const char16_t kClsid[] = u"\\CLSID\\";
46 static const char16_t kInprocServer32[] = u"\\InprocServer32";
47 static const char16_t kInprocHandler32[] = u"\\InprocHandler32";
48 static const char16_t kTypeLib[] = u"\\TypeLib";
49 static const char16_t kVersion[] = u"Version";
50 static const char16_t kWin32[] = u"Win32";
51 static const char16_t kWin64[] = u"Win64";
52
GetStringValue(HKEY aBaseKey,const nsAString & aStrSubKey,const nsAString & aValueName,nsAString & aOutput)53 static bool GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
54 const nsAString& aValueName, nsAString& aOutput) {
55 const nsString& flatSubKey = PromiseFlatString(aStrSubKey);
56 const nsString& flatValueName = PromiseFlatString(aValueName);
57 LPCWSTR valueName = aValueName.IsEmpty() ? nullptr : flatValueName.get();
58
59 DWORD type = 0;
60 DWORD numBytes = 0;
61 LONG result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, RRF_RT_ANY,
62 &type, nullptr, &numBytes);
63 if (result != ERROR_SUCCESS || (type != REG_SZ && type != REG_EXPAND_SZ)) {
64 return false;
65 }
66
67 int numChars = (numBytes + 1) / sizeof(wchar_t);
68 aOutput.SetLength(numChars);
69
70 DWORD acceptFlag = type == REG_SZ ? RRF_RT_REG_SZ : RRF_RT_REG_EXPAND_SZ;
71
72 result = RegGetValue(aBaseKey, flatSubKey.get(), valueName, acceptFlag,
73 nullptr, aOutput.BeginWriting(), &numBytes);
74 if (result == ERROR_SUCCESS) {
75 // Truncate null terminator
76 aOutput.SetLength(((numBytes + 1) / sizeof(wchar_t)) - 1);
77 }
78
79 return result == ERROR_SUCCESS;
80 }
81
82 template <size_t N>
GetStringValue(HKEY aBaseKey,const nsAString & aStrSubKey,const char16_t (& aValueName)[N],nsAString & aOutput)83 inline static bool GetStringValue(HKEY aBaseKey, const nsAString& aStrSubKey,
84 const char16_t (&aValueName)[N],
85 nsAString& aOutput) {
86 return GetStringValue(aBaseKey, aStrSubKey, nsLiteralString(aValueName),
87 aOutput);
88 }
89
90 /**
91 * This function fails unless the entire string has been converted.
92 * (eg, the string "FLAGS" will convert to 0xF but we will return false)
93 */
ConvertLCID(const wchar_t * aStr,NotNull<unsigned long * > aOutLcid)94 static bool ConvertLCID(const wchar_t* aStr, NotNull<unsigned long*> aOutLcid) {
95 wchar_t* endChar;
96 *aOutLcid = wcstoul(aStr, &endChar, 16);
97 return *endChar == 0;
98 }
99
GetLoadedPath(nsAString & aPath)100 static bool GetLoadedPath(nsAString& aPath) {
101 // These paths may be REG_EXPAND_SZ, so we expand any environment strings
102 DWORD bufCharLen =
103 ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), nullptr, 0);
104
105 auto buf = MakeUnique<WCHAR[]>(bufCharLen);
106
107 if (!ExpandEnvironmentStrings(PromiseFlatString(aPath).get(), buf.get(),
108 bufCharLen)) {
109 return false;
110 }
111
112 // Use LoadLibrary so that the DLL is resolved using the loader's DLL search
113 // rules
114 nsModuleHandle mod(LoadLibrary(buf.get()));
115 if (!mod) {
116 return false;
117 }
118
119 WCHAR finalPath[MAX_PATH + 1] = {};
120 DWORD result = GetModuleFileNameW(mod, finalPath, ArrayLength(finalPath));
121 if (!result || (result == ArrayLength(finalPath) &&
122 GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
123 return false;
124 }
125
126 aPath = nsDependentString(finalPath, result);
127 return true;
128 }
129
AnnotateClsidRegistrationForHive(JSONWriter & aJson,HKEY aHive,const nsAString & aClsid,const JSONWriter::CollectionStyle aStyle)130 static void AnnotateClsidRegistrationForHive(
131 JSONWriter& aJson, HKEY aHive, const nsAString& aClsid,
132 const JSONWriter::CollectionStyle aStyle) {
133 nsAutoString clsidSubkey;
134 clsidSubkey.AppendLiteral(kSoftwareClasses);
135 clsidSubkey.AppendLiteral(kClsid);
136 clsidSubkey.Append(aClsid);
137
138 nsAutoString className;
139 if (GetStringValue(aHive, clsidSubkey, kDefaultValue, className)) {
140 aJson.StringProperty("ClassName", NS_ConvertUTF16toUTF8(className));
141 }
142
143 nsAutoString inprocServerSubkey(clsidSubkey);
144 inprocServerSubkey.AppendLiteral(kInprocServer32);
145
146 nsAutoString pathToServerDll;
147 if (GetStringValue(aHive, inprocServerSubkey, kDefaultValue,
148 pathToServerDll)) {
149 aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(pathToServerDll));
150 if (GetLoadedPath(pathToServerDll)) {
151 aJson.StringProperty("LoadedPath",
152 NS_ConvertUTF16toUTF8(pathToServerDll));
153 }
154 }
155
156 nsAutoString apartment;
157 if (GetStringValue(aHive, inprocServerSubkey, kThreadingModel, apartment)) {
158 aJson.StringProperty("ThreadingModel", NS_ConvertUTF16toUTF8(apartment));
159 }
160
161 nsAutoString inprocHandlerSubkey(clsidSubkey);
162 inprocHandlerSubkey.AppendLiteral(kInprocHandler32);
163 nsAutoString pathToHandlerDll;
164 if (GetStringValue(aHive, inprocHandlerSubkey, kDefaultValue,
165 pathToHandlerDll)) {
166 aJson.StringProperty("HandlerPath",
167 NS_ConvertUTF16toUTF8(pathToHandlerDll));
168 if (GetLoadedPath(pathToHandlerDll)) {
169 aJson.StringProperty("LoadedHandlerPath",
170 NS_ConvertUTF16toUTF8(pathToHandlerDll));
171 }
172 }
173
174 nsAutoString handlerApartment;
175 if (GetStringValue(aHive, inprocHandlerSubkey, kThreadingModel,
176 handlerApartment)) {
177 aJson.StringProperty("HandlerThreadingModel",
178 NS_ConvertUTF16toUTF8(handlerApartment));
179 }
180 }
181
CheckTlbPath(JSONWriter & aJson,const nsAString & aTypelibPath)182 static void CheckTlbPath(JSONWriter& aJson, const nsAString& aTypelibPath) {
183 const nsString& flatPath = PromiseFlatString(aTypelibPath);
184 DWORD bufCharLen = ExpandEnvironmentStrings(flatPath.get(), nullptr, 0);
185
186 auto buf = MakeUnique<WCHAR[]>(bufCharLen);
187
188 if (!ExpandEnvironmentStrings(flatPath.get(), buf.get(), bufCharLen)) {
189 return;
190 }
191
192 // See whether this tlb can actually be loaded
193 RefPtr<ITypeLib> typeLib;
194 HRESULT hr = LoadTypeLibEx(buf.get(), REGKIND_NONE, getter_AddRefs(typeLib));
195
196 nsPrintfCString loadResult("0x%08X", hr);
197 aJson.StringProperty("LoadResult", loadResult);
198 }
199
200 template <size_t N>
AnnotateTypelibPlatform(JSONWriter & aJson,HKEY aBaseKey,const nsAString & aLcidSubkey,const char16_t (& aPlatform)[N],const JSONWriter::CollectionStyle aStyle)201 static void AnnotateTypelibPlatform(JSONWriter& aJson, HKEY aBaseKey,
202 const nsAString& aLcidSubkey,
203 const char16_t (&aPlatform)[N],
204 const JSONWriter::CollectionStyle aStyle) {
205 nsLiteralString platform(aPlatform);
206
207 nsAutoString fullSubkey(aLcidSubkey);
208 fullSubkey.AppendLiteral(kBackslash);
209 fullSubkey.Append(platform);
210
211 nsAutoString tlbPath;
212 if (GetStringValue(aBaseKey, fullSubkey, kDefaultValue, tlbPath)) {
213 aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(platform), aStyle);
214 aJson.StringProperty("Path", NS_ConvertUTF16toUTF8(tlbPath));
215 CheckTlbPath(aJson, tlbPath);
216 aJson.EndObject();
217 }
218 }
219
AnnotateTypelibRegistrationForHive(JSONWriter & aJson,HKEY aHive,const nsAString & aTypelibId,const nsAString & aTypelibVersion,const JSONWriter::CollectionStyle aStyle)220 static void AnnotateTypelibRegistrationForHive(
221 JSONWriter& aJson, HKEY aHive, const nsAString& aTypelibId,
222 const nsAString& aTypelibVersion,
223 const JSONWriter::CollectionStyle aStyle) {
224 nsAutoString typelibSubKey;
225 typelibSubKey.AppendLiteral(kSoftwareClasses);
226 typelibSubKey.AppendLiteral(kTypeLib);
227 typelibSubKey.AppendLiteral(kBackslash);
228 typelibSubKey.Append(aTypelibId);
229 typelibSubKey.AppendLiteral(kBackslash);
230 typelibSubKey.Append(aTypelibVersion);
231
232 nsAutoString typelibDesc;
233 if (GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibDesc)) {
234 aJson.StringProperty("Description", NS_ConvertUTF16toUTF8(typelibDesc));
235 }
236
237 nsAutoString flagsSubKey(typelibSubKey);
238 flagsSubKey.AppendLiteral(kBackslash);
239 flagsSubKey.AppendLiteral(kFlags);
240
241 nsAutoString typelibFlags;
242 if (GetStringValue(aHive, flagsSubKey, kDefaultValue, typelibFlags)) {
243 aJson.StringProperty("Flags", NS_ConvertUTF16toUTF8(typelibFlags));
244 }
245
246 HKEY rawTypelibKey;
247 LONG result =
248 RegOpenKeyEx(aHive, typelibSubKey.get(), 0, KEY_READ, &rawTypelibKey);
249 if (result != ERROR_SUCCESS) {
250 return;
251 }
252 nsAutoRegKey typelibKey(rawTypelibKey);
253
254 const size_t kMaxLcidCharLen = 9;
255 WCHAR keyName[kMaxLcidCharLen];
256
257 for (DWORD index = 0; result == ERROR_SUCCESS; ++index) {
258 DWORD keyNameLength = ArrayLength(keyName);
259 result = RegEnumKeyEx(typelibKey, index, keyName, &keyNameLength, nullptr,
260 nullptr, nullptr, nullptr);
261
262 unsigned long lcid;
263 if (result == ERROR_SUCCESS && ConvertLCID(keyName, WrapNotNull(&lcid))) {
264 nsDependentString strLcid(keyName, keyNameLength);
265 aJson.StartObjectProperty(NS_ConvertUTF16toUTF8(strLcid), aStyle);
266 AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin32, aStyle);
267 #if defined(HAVE_64BIT_BUILD)
268 AnnotateTypelibPlatform(aJson, typelibKey, strLcid, kWin64, aStyle);
269 #endif
270 aJson.EndObject();
271 }
272 }
273 }
274
AnnotateInterfaceRegistrationForHive(JSONWriter & aJson,HKEY aHive,REFIID aIid,const JSONWriter::CollectionStyle aStyle)275 static void AnnotateInterfaceRegistrationForHive(
276 JSONWriter& aJson, HKEY aHive, REFIID aIid,
277 const JSONWriter::CollectionStyle aStyle) {
278 nsAutoString interfaceSubKey;
279 interfaceSubKey.AppendLiteral(kSoftwareClasses);
280 interfaceSubKey.AppendLiteral(kInterface);
281 nsAutoString iid;
282 GUIDToString(aIid, iid);
283 interfaceSubKey.Append(iid);
284
285 nsAutoString interfaceName;
286 if (GetStringValue(aHive, interfaceSubKey, kDefaultValue, interfaceName)) {
287 aJson.StringProperty("InterfaceName", NS_ConvertUTF16toUTF8(interfaceName));
288 }
289
290 nsAutoString psSubKey(interfaceSubKey);
291 psSubKey.AppendLiteral(kProxyStubClsid32);
292
293 nsAutoString psClsid;
294 if (GetStringValue(aHive, psSubKey, kDefaultValue, psClsid)) {
295 aJson.StartObjectProperty("ProxyStub", aStyle);
296 aJson.StringProperty("CLSID", NS_ConvertUTF16toUTF8(psClsid));
297 AnnotateClsidRegistrationForHive(aJson, aHive, psClsid, aStyle);
298 aJson.EndObject();
299 }
300
301 nsAutoString typelibSubKey(interfaceSubKey);
302 typelibSubKey.AppendLiteral(kTypeLib);
303
304 nsAutoString typelibId;
305 bool haveTypelibId =
306 GetStringValue(aHive, typelibSubKey, kDefaultValue, typelibId);
307
308 nsAutoString typelibVersion;
309 bool haveTypelibVersion =
310 GetStringValue(aHive, typelibSubKey, kVersion, typelibVersion);
311
312 if (haveTypelibId || haveTypelibVersion) {
313 aJson.StartObjectProperty("TypeLib", aStyle);
314 }
315
316 if (haveTypelibId) {
317 aJson.StringProperty("ID", NS_ConvertUTF16toUTF8(typelibId));
318 }
319
320 if (haveTypelibVersion) {
321 aJson.StringProperty("Version", NS_ConvertUTF16toUTF8(typelibVersion));
322 }
323
324 if (haveTypelibId && haveTypelibVersion) {
325 AnnotateTypelibRegistrationForHive(aJson, aHive, typelibId, typelibVersion,
326 aStyle);
327 }
328
329 if (haveTypelibId || haveTypelibVersion) {
330 aJson.EndObject();
331 }
332 }
333
AnnotateInterfaceRegistration(REFIID aIid)334 void AnnotateInterfaceRegistration(REFIID aIid) {
335 #if defined(DEBUG)
336 const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
337 #else
338 const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
339 #endif
340
341 JSONWriter json(MakeUnique<CStringWriter>());
342
343 json.Start(style);
344
345 json.StartObjectProperty("HKLM", style);
346 AnnotateInterfaceRegistrationForHive(json, HKEY_LOCAL_MACHINE, aIid, style);
347 json.EndObject();
348
349 json.StartObjectProperty("HKCU", style);
350 AnnotateInterfaceRegistrationForHive(json, HKEY_CURRENT_USER, aIid, style);
351 json.EndObject();
352
353 json.End();
354
355 CrashReporter::Annotation annotationKey;
356 if (XRE_IsParentProcess()) {
357 annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoParent;
358 } else {
359 annotationKey = CrashReporter::Annotation::InterfaceRegistrationInfoChild;
360 }
361 CrashReporter::AnnotateCrashReport(
362 annotationKey, static_cast<CStringWriter*>(json.WriteFunc())->Get());
363 }
364
AnnotateClassRegistration(REFCLSID aClsid)365 void AnnotateClassRegistration(REFCLSID aClsid) {
366 #if defined(DEBUG)
367 const JSONWriter::CollectionStyle style = JSONWriter::MultiLineStyle;
368 #else
369 const JSONWriter::CollectionStyle style = JSONWriter::SingleLineStyle;
370 #endif
371
372 nsAutoString strClsid;
373 GUIDToString(aClsid, strClsid);
374
375 JSONWriter json(MakeUnique<CStringWriter>());
376
377 json.Start(style);
378
379 json.StartObjectProperty("HKLM", style);
380 AnnotateClsidRegistrationForHive(json, HKEY_LOCAL_MACHINE, strClsid, style);
381 json.EndObject();
382
383 json.StartObjectProperty("HKCU", style);
384 AnnotateClsidRegistrationForHive(json, HKEY_CURRENT_USER, strClsid, style);
385 json.EndObject();
386
387 json.End();
388
389 CrashReporter::Annotation annotationKey;
390 if (XRE_IsParentProcess()) {
391 annotationKey = CrashReporter::Annotation::ClassRegistrationInfoParent;
392 } else {
393 annotationKey = CrashReporter::Annotation::ClassRegistrationInfoChild;
394 }
395
396 CrashReporter::AnnotateCrashReport(
397 annotationKey, static_cast<CStringWriter*>(json.WriteFunc())->Get());
398 }
399
400 } // namespace mscom
401 } // namespace mozilla
402