1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #ifdef MOZ_MEMORY
7 #define MOZ_MEMORY_IMPL
8 #include "mozmemory_wrap.h"
9 #define MALLOC_FUNCS MALLOC_FUNCS_MALLOC
10 // See mozmemory_wrap.h for more details. This file is part of libmozglue, so
11 // it needs to use _impl suffixes.
12 #define MALLOC_DECL(name, return_type, ...) \
13 MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__);
14 #include "malloc_decls.h"
15 #endif
16
17 #include <windows.h>
18 #include <winternl.h>
19 #include <io.h>
20
21 #pragma warning(push)
22 #pragma warning(disable : 4275 4530) // See msvc-stl-wrapper.template.h
23 #include <map>
24 #pragma warning(pop)
25
26 #include "Authenticode.h"
27 #include "nsAutoPtr.h"
28 #include "nsWindowsDllInterceptor.h"
29 #include "mozilla/Sprintf.h"
30 #include "mozilla/StackWalk_windows.h"
31 #include "mozilla/UniquePtr.h"
32 #include "mozilla/WindowsVersion.h"
33 #include "nsWindowsHelpers.h"
34 #include "WindowsDllBlocklist.h"
35 #include "mozilla/AutoProfilerLabel.h"
36 #include "mozilla/glue/WindowsDllServices.h"
37
38 using namespace mozilla;
39
40 #define ALL_VERSIONS ((unsigned long long)-1LL)
41
42 // DLLs sometimes ship without a version number, particularly early
43 // releases. Blocking "version <= 0" has the effect of blocking unversioned
44 // DLLs (since the call to get version info fails), but not blocking
45 // any versioned instance.
46 #define UNVERSIONED ((unsigned long long)0LL)
47
48 // Convert the 4 (decimal) components of a DLL version number into a
49 // single unsigned long long, as needed by the blocklist
50 #define MAKE_VERSION(a, b, c, d) \
51 ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL)
52
53 struct DllBlockInfo {
54 // The name of the DLL -- in LOWERCASE! It will be compared to
55 // a lowercase version of the DLL name only.
56 const char* name;
57
58 // If maxVersion is ALL_VERSIONS, we'll block all versions of this
59 // dll. Otherwise, we'll block all versions less than or equal to
60 // the given version, as queried by GetFileVersionInfo and
61 // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields.
62 //
63 // Note that the version is usually 4 components, which is A.B.C.D
64 // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity),
65 // but it's not required to be of that format.
66 //
67 // If the USE_TIMESTAMP flag is set, then we use the timestamp from
68 // the IMAGE_FILE_HEADER in lieu of a version number.
69 //
70 // If the CHILD_PROCESSES_ONLY flag is set, then the dll is blocked
71 // only when we are a child process.
72 unsigned long long maxVersion;
73
74 enum {
75 FLAGS_DEFAULT = 0,
76 BLOCK_WIN8PLUS_ONLY = 1,
77 BLOCK_WIN8_ONLY = 2,
78 USE_TIMESTAMP = 4,
79 CHILD_PROCESSES_ONLY = 8
80 } flags;
81 };
82
83 static const DllBlockInfo sWindowsDllBlocklist[] = {
84 // EXAMPLE:
85 // { "uxtheme.dll", ALL_VERSIONS },
86 // { "uxtheme.dll", 0x0000123400000000ULL },
87 // The DLL name must be in lowercase!
88 // The version field is a maximum, that is, we block anything that is
89 // less-than or equal to that version.
90
91 // NPFFAddon - Known malware
92 {"npffaddon.dll", ALL_VERSIONS},
93
94 // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted
95 {"avgrsstx.dll", MAKE_VERSION(8, 5, 0, 401)},
96
97 // calc.dll - Suspected malware
98 {"calc.dll", MAKE_VERSION(1, 0, 0, 1)},
99
100 // hook.dll - Suspected malware
101 {"hook.dll", ALL_VERSIONS},
102
103 // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances
104 // of this DLL cause crashes
105 {"googledesktopnetwork3.dll", UNVERSIONED},
106
107 // rdolib.dll - Suspected malware
108 {"rdolib.dll", MAKE_VERSION(6, 0, 88, 4)},
109
110 // fgjk4wvb.dll - Suspected malware
111 {"fgjk4wvb.dll", MAKE_VERSION(8, 8, 8, 8)},
112
113 // radhslib.dll - Naomi internet filter - unmaintained since 2006
114 {"radhslib.dll", UNVERSIONED},
115
116 // Music download filter for vkontakte.ru - old instances
117 // of this DLL cause crashes
118 {"vksaver.dll", MAKE_VERSION(2, 2, 2, 0)},
119
120 // Topcrash in Firefox 4.0b1
121 {"rlxf.dll", MAKE_VERSION(1, 2, 323, 1)},
122
123 // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox
124 // Adobe photoshop library, now redundant in later installations
125 {"psicon.dll", ALL_VERSIONS},
126
127 // Topcrash in Firefox 4 betas (bug 618899)
128 {"accelerator.dll", MAKE_VERSION(3, 2, 1, 6)},
129
130 // Topcrash with Roboform in Firefox 8 (bug 699134)
131 {"rf-firefox.dll", MAKE_VERSION(7, 6, 1, 0)},
132 {"roboform.dll", MAKE_VERSION(7, 6, 1, 0)},
133
134 // Topcrash with Babylon Toolbar on FF16+ (bug 721264)
135 {"babyfox.dll", ALL_VERSIONS},
136
137 // sprotector.dll crashes, bug 957258
138 {"sprotector.dll", ALL_VERSIONS},
139
140 // leave these two in always for tests
141 {"mozdllblockingtest.dll", ALL_VERSIONS},
142 {"mozdllblockingtest_versioned.dll", 0x0000000400000000ULL},
143
144 // Windows Media Foundation FLAC decoder and type sniffer (bug 839031).
145 {"mfflac.dll", ALL_VERSIONS},
146
147 // Older Relevant Knowledge DLLs cause us to crash (bug 904001).
148 {"rlnx.dll", MAKE_VERSION(1, 3, 334, 9)},
149 {"pmnx.dll", MAKE_VERSION(1, 3, 334, 9)},
150 {"opnx.dll", MAKE_VERSION(1, 3, 334, 9)},
151 {"prnx.dll", MAKE_VERSION(1, 3, 334, 9)},
152
153 // Older belgian ID card software causes Firefox to crash or hang on
154 // shutdown, bug 831285 and 918399.
155 {"beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968)},
156
157 // bug 925459, bitguard crashes
158 {"bitguard.dll", ALL_VERSIONS},
159
160 // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed
161 // Software is discontinued/unsupported
162 {"atkdx11disp.dll", ALL_VERSIONS},
163
164 // Topcrash with Conduit SearchProtect, bug 944542
165 {"spvc32.dll", ALL_VERSIONS},
166
167 // Topcrash with V-bates, bug 1002748 and bug 1023239
168 {"libinject.dll", UNVERSIONED},
169 {"libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP},
170 {"libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP},
171
172 // Crashes with RoboForm2Go written against old SDK, bug 988311/1196859
173 {"rf-firefox-22.dll", ALL_VERSIONS},
174 {"rf-firefox-40.dll", ALL_VERSIONS},
175
176 // Crashes with DesktopTemperature, bug 1046382
177 {"dtwxsvc.dll", 0x53153234, DllBlockInfo::USE_TIMESTAMP},
178
179 // Startup crashes with Lenovo Onekey Theater, bug 1123778
180 {"activedetect32.dll", UNVERSIONED},
181 {"activedetect64.dll", UNVERSIONED},
182 {"windowsapihookdll32.dll", UNVERSIONED},
183 {"windowsapihookdll64.dll", UNVERSIONED},
184
185 // Flash crashes with RealNetworks RealDownloader, bug 1132663
186 {"rndlnpshimswf.dll", ALL_VERSIONS},
187 {"rndlmainbrowserrecordplugin.dll", ALL_VERSIONS},
188
189 // Startup crashes with RealNetworks Browser Record Plugin, bug 1170141
190 {"nprpffbrowserrecordext.dll", ALL_VERSIONS},
191 {"nprndlffbrowserrecordext.dll", ALL_VERSIONS},
192
193 // Crashes with CyberLink YouCam, bug 1136968
194 {"ycwebcamerasource.ax", MAKE_VERSION(2, 0, 0, 1611)},
195
196 // Old version of WebcamMax crashes WebRTC, bug 1130061
197 {"vwcsource.ax", MAKE_VERSION(1, 5, 0, 0)},
198
199 // NetOp School, discontinued product, bug 763395
200 {"nlsp.dll", MAKE_VERSION(6, 23, 2012, 19)},
201
202 // Orbit Downloader, bug 1222819
203 {"grabdll.dll", MAKE_VERSION(2, 6, 1, 0)},
204 {"grabkernel.dll", MAKE_VERSION(1, 0, 0, 1)},
205
206 // ESET, bug 1229252
207 {"eoppmonitor.dll", ALL_VERSIONS},
208
209 // SS2OSD, bug 1262348
210 {"ss2osd.dll", ALL_VERSIONS},
211 {"ss2devprops.dll", ALL_VERSIONS},
212
213 // NHASUSSTRIXOSD.DLL, bug 1269244
214 {"nhasusstrixosd.dll", ALL_VERSIONS},
215 {"nhasusstrixdevprops.dll", ALL_VERSIONS},
216
217 // Crashes with PremierOpinion/RelevantKnowledge, bug 1277846
218 {"opls.dll", ALL_VERSIONS},
219 {"opls64.dll", ALL_VERSIONS},
220 {"pmls.dll", ALL_VERSIONS},
221 {"pmls64.dll", ALL_VERSIONS},
222 {"prls.dll", ALL_VERSIONS},
223 {"prls64.dll", ALL_VERSIONS},
224 {"rlls.dll", ALL_VERSIONS},
225 {"rlls64.dll", ALL_VERSIONS},
226
227 // Vorbis DirectShow filters, bug 1239690.
228 {"vorbis.acm", MAKE_VERSION(0, 0, 3, 6)},
229
230 // AhnLab Internet Security, bug 1311969
231 {"nzbrcom.dll", ALL_VERSIONS},
232
233 // K7TotalSecurity, bug 1339083.
234 {"k7pswsen.dll", MAKE_VERSION(15, 2, 2, 95)},
235
236 // smci*.dll - goobzo crashware (bug 1339908)
237 {"smci32.dll", ALL_VERSIONS},
238 {"smci64.dll", ALL_VERSIONS},
239
240 // Crashes with Internet Download Manager, bug 1333486
241 {"idmcchandler7.dll", ALL_VERSIONS},
242 {"idmcchandler7_64.dll", ALL_VERSIONS},
243 {"idmcchandler5.dll", ALL_VERSIONS},
244 {"idmcchandler5_64.dll", ALL_VERSIONS},
245
246 // Nahimic 2 breaks applicaton update (bug 1356637)
247 {"nahimic2devprops.dll", MAKE_VERSION(2, 5, 19, 0xffff)},
248 // Nahimic is causing crashes, bug 1233556
249 {"nahimicmsiosd.dll", UNVERSIONED},
250 // Nahimic is causing crashes, bug 1360029
251 {"nahimicvrdevprops.dll", UNVERSIONED},
252 {"nahimic2osd.dll", MAKE_VERSION(2, 5, 19, 0xffff)},
253 {"nahimicmsidevprops.dll", UNVERSIONED},
254
255 // Bug 1268470 - crashes with Kaspersky Lab on Windows 8
256 {"klsihk64.dll", MAKE_VERSION(14, 0, 456, 0xffff),
257 DllBlockInfo::BLOCK_WIN8_ONLY},
258
259 // Bug 1407337, crashes with OpenSC < 0.16.0
260 {"onepin-opensc-pkcs11.dll", MAKE_VERSION(0, 15, 0xffff, 0xffff)},
261
262 // Avecto Privilege Guard causes crashes, bug 1385542
263 {"pghook.dll", ALL_VERSIONS},
264
265 // Old versions of G DATA BankGuard, bug 1421991
266 {"banksafe64.dll", MAKE_VERSION(1, 2, 15299, 65535)},
267
268 // Old versions of G DATA, bug 1043775
269 {"gdkbfltdll64.dll", MAKE_VERSION(1, 0, 14141, 240)},
270
271 // NVIDIA nView Desktop Management causes crashes, bug 1465787
272 {"nviewh64.dll", MAKE_VERSION(6, 14, 10, 14847)},
273
274 {nullptr, 0}};
275
276 #ifndef STATUS_DLL_NOT_FOUND
277 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
278 #endif
279
280 // define this for very verbose dll load debug spew
281 #undef DEBUG_very_verbose
282
283 static const char kBlockedDllsParameter[] = "BlockedDllList=";
284 static const int kBlockedDllsParameterLen = sizeof(kBlockedDllsParameter) - 1;
285
286 static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n";
287 static const int kBlocklistInitFailedParameterLen =
288 sizeof(kBlocklistInitFailedParameter) - 1;
289
290 static const char kUser32BeforeBlocklistParameter[] =
291 "User32BeforeBlocklist=1\n";
292 static const int kUser32BeforeBlocklistParameterLen =
293 sizeof(kUser32BeforeBlocklistParameter) - 1;
294
295 static uint32_t sInitFlags;
296 static bool sBlocklistInitAttempted;
297 static bool sBlocklistInitFailed;
298 static bool sUser32BeforeBlocklist;
299
300 // Duplicated from xpcom glue. Ideally this should be shared.
printf_stderr(const char * fmt,...)301 void printf_stderr(const char* fmt, ...) {
302 if (IsDebuggerPresent()) {
303 char buf[2048];
304 va_list args;
305 va_start(args, fmt);
306 VsprintfLiteral(buf, fmt, args);
307 va_end(args);
308 OutputDebugStringA(buf);
309 }
310
311 FILE* fp = _fdopen(_dup(2), "a");
312 if (!fp) return;
313
314 va_list args;
315 va_start(args, fmt);
316 vfprintf(fp, fmt, args);
317 va_end(args);
318
319 fclose(fp);
320 }
321
322 typedef MOZ_NORETURN_PTR void(__fastcall* BaseThreadInitThunk_func)(
323 BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
324 static BaseThreadInitThunk_func stub_BaseThreadInitThunk = nullptr;
325
326 typedef NTSTATUS(NTAPI* LdrLoadDll_func)(PWCHAR filePath, PULONG flags,
327 PUNICODE_STRING moduleFileName,
328 PHANDLE handle);
329 static LdrLoadDll_func stub_LdrLoadDll;
330
331 #ifdef _M_AMD64
332 typedef decltype(
333 RtlInstallFunctionTableCallback)* RtlInstallFunctionTableCallback_func;
334 static RtlInstallFunctionTableCallback_func
335 stub_RtlInstallFunctionTableCallback;
336
337 extern uint8_t* sMsMpegJitCodeRegionStart;
338 extern size_t sMsMpegJitCodeRegionSize;
339
patched_RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,DWORD64 BaseAddress,DWORD Length,PGET_RUNTIME_FUNCTION_CALLBACK Callback,PVOID Context,PCWSTR OutOfProcessCallbackDll)340 BOOLEAN WINAPI patched_RtlInstallFunctionTableCallback(
341 DWORD64 TableIdentifier, DWORD64 BaseAddress, DWORD Length,
342 PGET_RUNTIME_FUNCTION_CALLBACK Callback, PVOID Context,
343 PCWSTR OutOfProcessCallbackDll) {
344 // msmpeg2vdec.dll sets up a function table callback for their JIT code that
345 // just terminates the process, because their JIT doesn't have unwind info.
346 // If we see this callback being registered, record the region address, so
347 // that StackWalk.cpp can avoid unwinding addresses in this region.
348 //
349 // To keep things simple I'm not tracking unloads of msmpeg2vdec.dll.
350 // Worst case the stack walker will needlessly avoid a few pages of memory.
351
352 // Tricky: GetModuleHandleExW adds a ref by default; GetModuleHandleW doesn't.
353 HMODULE callbackModule = nullptr;
354 DWORD moduleFlags = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
355 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT;
356
357 // These GetModuleHandle calls enter a critical section on Win7.
358 AutoSuppressStackWalking suppress;
359
360 if (GetModuleHandleExW(moduleFlags, (LPWSTR)Callback, &callbackModule) &&
361 GetModuleHandleW(L"msmpeg2vdec.dll") == callbackModule) {
362 sMsMpegJitCodeRegionStart = (uint8_t*)BaseAddress;
363 sMsMpegJitCodeRegionSize = Length;
364 }
365
366 return stub_RtlInstallFunctionTableCallback(TableIdentifier, BaseAddress,
367 Length, Callback, Context,
368 OutOfProcessCallbackDll);
369 }
370 #endif
371
372 template <class T>
373 struct RVAMap {
RVAMapRVAMap374 RVAMap(HANDLE map, DWORD offset) {
375 SYSTEM_INFO info;
376 GetSystemInfo(&info);
377
378 DWORD alignedOffset =
379 (offset / info.dwAllocationGranularity) * info.dwAllocationGranularity;
380
381 MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf");
382
383 mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset,
384 sizeof(T) + (offset - alignedOffset));
385
386 mMappedView =
387 mRealView
388 ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset))
389 : nullptr;
390 }
~RVAMapRVAMap391 ~RVAMap() {
392 if (mRealView) {
393 ::UnmapViewOfFile(mRealView);
394 }
395 }
operator const T*RVAMap396 operator const T*() const { return mMappedView; }
operator ->RVAMap397 const T* operator->() const { return mMappedView; }
398
399 private:
400 const T* mMappedView;
401 void* mRealView;
402 };
403
GetTimestamp(const wchar_t * path)404 static DWORD GetTimestamp(const wchar_t* path) {
405 DWORD timestamp = 0;
406
407 HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr,
408 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
409 if (file != INVALID_HANDLE_VALUE) {
410 HANDLE map =
411 ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
412 if (map) {
413 RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
414 if (peHeader) {
415 RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
416 if (ntHeader) {
417 timestamp = ntHeader->FileHeader.TimeDateStamp;
418 }
419 }
420 ::CloseHandle(map);
421 }
422 ::CloseHandle(file);
423 }
424
425 return timestamp;
426 }
427
428 // This lock protects both the reentrancy sentinel and the crash reporter
429 // data structures.
430 static CRITICAL_SECTION sLock;
431
432 /**
433 * Some versions of Windows call LoadLibraryEx to get the version information
434 * for a DLL, which causes our patched LdrLoadDll implementation to re-enter
435 * itself and cause infinite recursion and a stack-exhaustion crash. We protect
436 * against reentrancy by allowing recursive loads of the same DLL.
437 *
438 * Note that we don't use __declspec(thread) because that doesn't work in DLLs
439 * loaded via LoadLibrary and there can be a limited number of TLS slots, so
440 * we roll our own.
441 */
442 class ReentrancySentinel {
443 public:
ReentrancySentinel(const char * dllName)444 explicit ReentrancySentinel(const char* dllName) {
445 DWORD currentThreadId = GetCurrentThreadId();
446 AutoCriticalSection lock(&sLock);
447 mPreviousDllName = (*sThreadMap)[currentThreadId];
448
449 // If there is a DLL currently being loaded and it has the same name
450 // as the current attempt, we're re-entering.
451 mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName);
452 (*sThreadMap)[currentThreadId] = dllName;
453 }
454
~ReentrancySentinel()455 ~ReentrancySentinel() {
456 DWORD currentThreadId = GetCurrentThreadId();
457 AutoCriticalSection lock(&sLock);
458 (*sThreadMap)[currentThreadId] = mPreviousDllName;
459 }
460
BailOut() const461 bool BailOut() const { return mReentered; };
462
InitializeStatics()463 static void InitializeStatics() {
464 InitializeCriticalSection(&sLock);
465 sThreadMap = new std::map<DWORD, const char*>;
466 }
467
468 private:
469 static std::map<DWORD, const char*>* sThreadMap;
470
471 const char* mPreviousDllName;
472 bool mReentered;
473 };
474
475 std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
476
477 /**
478 * This is a linked list of DLLs that have been blocked. It doesn't use
479 * mozilla::LinkedList because this is an append-only list and doesn't need
480 * to be doubly linked.
481 */
482 class DllBlockSet {
483 public:
484 static void Add(const char* name, unsigned long long version);
485
486 // Write the list of blocked DLLs to a file HANDLE. This method is run after
487 // a crash occurs and must therefore not use the heap, etc.
488 static void Write(HANDLE file);
489
490 private:
DllBlockSet(const char * name,unsigned long long version)491 DllBlockSet(const char* name, unsigned long long version)
492 : mName(name), mVersion(version), mNext(nullptr) {}
493
494 const char* mName; // points into the sWindowsDllBlocklist string
495 unsigned long long mVersion;
496 DllBlockSet* mNext;
497
498 static DllBlockSet* gFirst;
499 };
500
501 DllBlockSet* DllBlockSet::gFirst;
502
Add(const char * name,unsigned long long version)503 void DllBlockSet::Add(const char* name, unsigned long long version) {
504 AutoCriticalSection lock(&sLock);
505 for (DllBlockSet* b = gFirst; b; b = b->mNext) {
506 if (0 == strcmp(b->mName, name) && b->mVersion == version) {
507 return;
508 }
509 }
510 // Not already present
511 DllBlockSet* n = new DllBlockSet(name, version);
512 n->mNext = gFirst;
513 gFirst = n;
514 }
515
Write(HANDLE file)516 void DllBlockSet::Write(HANDLE file) {
517 // It would be nicer to use AutoCriticalSection here. However, its destructor
518 // might not run if an exception occurs, in which case we would never leave
519 // the critical section. (MSVC warns about this possibility.) So we
520 // enter and leave manually.
521 ::EnterCriticalSection(&sLock);
522
523 // Because this method is called after a crash occurs, and uses heap memory,
524 // protect this entire block with a structured exception handler.
525 MOZ_SEH_TRY {
526 DWORD nBytes;
527 for (DllBlockSet* b = gFirst; b; b = b->mNext) {
528 // write name[,v.v.v.v];
529 WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
530 if (b->mVersion != ALL_VERSIONS) {
531 WriteFile(file, ",", 1, &nBytes, nullptr);
532 uint16_t parts[4];
533 parts[0] = b->mVersion >> 48;
534 parts[1] = (b->mVersion >> 32) & 0xFFFF;
535 parts[2] = (b->mVersion >> 16) & 0xFFFF;
536 parts[3] = b->mVersion & 0xFFFF;
537 for (int p = 0; p < 4; ++p) {
538 char buf[32];
539 ltoa(parts[p], buf, 10);
540 WriteFile(file, buf, strlen(buf), &nBytes, nullptr);
541 if (p != 3) {
542 WriteFile(file, ".", 1, &nBytes, nullptr);
543 }
544 }
545 }
546 WriteFile(file, ";", 1, &nBytes, nullptr);
547 }
548 }
549 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {}
550
551 ::LeaveCriticalSection(&sLock);
552 }
553
getFullPath(PWCHAR filePath,wchar_t * fname)554 static UniquePtr<wchar_t[]> getFullPath(PWCHAR filePath, wchar_t* fname) {
555 // In Windows 8, the first parameter seems to be used for more than just the
556 // path name. For example, its numerical value can be 1. Passing a non-valid
557 // pointer to SearchPathW will cause a crash, so we need to check to see if we
558 // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW.
559 PWCHAR sanitizedFilePath = nullptr;
560 if ((uintptr_t(filePath) >= 65536) && ((uintptr_t(filePath) & 1) == 0)) {
561 sanitizedFilePath = filePath;
562 }
563
564 // figure out the length of the string that we need
565 DWORD pathlen =
566 SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr, nullptr);
567 if (pathlen == 0) {
568 return nullptr;
569 }
570
571 auto full_fname = MakeUnique<wchar_t[]>(pathlen + 1);
572 if (!full_fname) {
573 // couldn't allocate memory?
574 return nullptr;
575 }
576
577 // now actually grab it
578 SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname.get(),
579 nullptr);
580 return full_fname;
581 }
582
583 // No builtin function to find the last character matching a set
lastslash(wchar_t * s,int len)584 static wchar_t* lastslash(wchar_t* s, int len) {
585 for (wchar_t* c = s + len - 1; c >= s; --c) {
586 if (*c == L'\\' || *c == L'/') {
587 return c;
588 }
589 }
590 return nullptr;
591 }
592
patched_LdrLoadDll(PWCHAR filePath,PULONG flags,PUNICODE_STRING moduleFileName,PHANDLE handle)593 static NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR filePath, PULONG flags,
594 PUNICODE_STRING moduleFileName,
595 PHANDLE handle) {
596 // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename
597 // portion
598 #define DLLNAME_MAX 128
599 char dllName[DLLNAME_MAX + 1];
600 wchar_t* dll_part;
601 char* dot;
602
603 int len = moduleFileName->Length / 2;
604 wchar_t* fname = moduleFileName->Buffer;
605 UniquePtr<wchar_t[]> full_fname;
606
607 const DllBlockInfo* info = &sWindowsDllBlocklist[0];
608
609 // The filename isn't guaranteed to be null terminated, but in practice
610 // it always will be; ensure that this is so, and bail if not.
611 // This is done instead of the more robust approach because of bug 527122,
612 // where lots of weird things were happening when we tried to make a copy.
613 if (moduleFileName->MaximumLength < moduleFileName->Length + 2 ||
614 fname[len] != 0) {
615 #ifdef DEBUG
616 printf_stderr("LdrLoadDll: non-null terminated string found!\n");
617 #endif
618 goto continue_loading;
619 }
620
621 dll_part = lastslash(fname, len);
622 if (dll_part) {
623 dll_part = dll_part + 1;
624 len -= dll_part - fname;
625 } else {
626 dll_part = fname;
627 }
628
629 #ifdef DEBUG_very_verbose
630 printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len);
631 #endif
632
633 // if it's too long, then, we assume we won't want to block it,
634 // since DLLNAME_MAX should be at least long enough to hold the longest
635 // entry in our blocklist.
636 if (len > DLLNAME_MAX) {
637 #ifdef DEBUG
638 printf_stderr("LdrLoadDll: len too long! %d\n", len);
639 #endif
640 goto continue_loading;
641 }
642
643 // copy over to our char byte buffer, lowercasing ASCII as we go
644 for (int i = 0; i < len; i++) {
645 wchar_t c = dll_part[i];
646
647 if (c > 0x7f) {
648 // welp, it's not ascii; if we need to add non-ascii things to
649 // our blocklist, we'll have to remove this limitation.
650 goto continue_loading;
651 }
652
653 // ensure that dll name is all lowercase
654 if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
655
656 dllName[i] = (char)c;
657 }
658
659 dllName[len] = 0;
660
661 #ifdef DEBUG_very_verbose
662 printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
663 #endif
664
665 // Block a suspicious binary that uses various 12-digit hex strings
666 // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
667 dot = strchr(dllName, '.');
668 if (dot && (strchr(dot + 1, '.') == dot + 13)) {
669 char* end = nullptr;
670 _strtoui64(dot + 1, &end, 16);
671 if (end == dot + 13) {
672 return STATUS_DLL_NOT_FOUND;
673 }
674 }
675 // Block binaries where the filename is at least 16 hex digits
676 if (dot && ((dot - dllName) >= 16)) {
677 char* current = dllName;
678 while (current < dot && isxdigit(*current)) {
679 current++;
680 }
681 if (current == dot) {
682 return STATUS_DLL_NOT_FOUND;
683 }
684 }
685
686 // then compare to everything on the blocklist
687 while (info->name) {
688 if (strcmp(info->name, dllName) == 0) break;
689
690 info++;
691 }
692
693 if (info->name) {
694 bool load_ok = false;
695
696 #ifdef DEBUG_very_verbose
697 printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
698 #endif
699
700 if ((info->flags & DllBlockInfo::BLOCK_WIN8PLUS_ONLY) && !IsWin8OrLater()) {
701 goto continue_loading;
702 }
703
704 if ((info->flags & DllBlockInfo::BLOCK_WIN8_ONLY) &&
705 (!IsWin8OrLater() || IsWin8Point1OrLater())) {
706 goto continue_loading;
707 }
708
709 if ((info->flags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
710 !(sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
711 goto continue_loading;
712 }
713
714 unsigned long long fVersion = ALL_VERSIONS;
715
716 if (info->maxVersion != ALL_VERSIONS) {
717 ReentrancySentinel sentinel(dllName);
718 if (sentinel.BailOut()) {
719 goto continue_loading;
720 }
721
722 full_fname = getFullPath(filePath, fname);
723 if (!full_fname) {
724 // uh, we couldn't find the DLL at all, so...
725 printf_stderr(
726 "LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n",
727 dllName);
728 return STATUS_DLL_NOT_FOUND;
729 }
730
731 if (info->flags & DllBlockInfo::USE_TIMESTAMP) {
732 fVersion = GetTimestamp(full_fname.get());
733 if (fVersion > info->maxVersion) {
734 load_ok = true;
735 }
736 } else {
737 DWORD zero;
738 DWORD infoSize = GetFileVersionInfoSizeW(full_fname.get(), &zero);
739
740 // If we failed to get the version information, we block.
741
742 if (infoSize != 0) {
743 auto infoData = MakeUnique<unsigned char[]>(infoSize);
744 VS_FIXEDFILEINFO* vInfo;
745 UINT vInfoLen;
746
747 if (GetFileVersionInfoW(full_fname.get(), 0, infoSize,
748 infoData.get()) &&
749 VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo,
750 &vInfoLen)) {
751 fVersion = ((unsigned long long)vInfo->dwFileVersionMS) << 32 |
752 ((unsigned long long)vInfo->dwFileVersionLS);
753
754 // finally do the version check, and if it's greater than our block
755 // version, keep loading
756 if (fVersion > info->maxVersion) load_ok = true;
757 }
758 }
759 }
760 }
761
762 if (!load_ok) {
763 printf_stderr(
764 "LdrLoadDll: Blocking load of '%s' -- see "
765 "http://www.mozilla.com/en-US/blocklist/\n",
766 dllName);
767 DllBlockSet::Add(info->name, fVersion);
768 return STATUS_DLL_NOT_FOUND;
769 }
770 }
771
772 continue_loading:
773 #ifdef DEBUG_very_verbose
774 printf_stderr("LdrLoadDll: continuing load... ('%S')\n",
775 moduleFileName->Buffer);
776 #endif
777
778 // A few DLLs such as xul.dll and nss3.dll get loaded before mozglue's
779 // AutoProfilerLabel is initialized, and this is a no-op in those cases. But
780 // the vast majority of DLLs do get labelled here.
781 AutoProfilerLabel label("WindowsDllBlocklist::patched_LdrLoadDll", dllName,
782 __LINE__);
783
784 #ifdef _M_AMD64
785 // Prevent the stack walker from suspending this thread when LdrLoadDll
786 // holds the RtlLookupFunctionEntry lock.
787 AutoSuppressStackWalking suppress;
788 #endif
789
790 return stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
791 }
792
ShouldBlockThread(void * aStartAddress)793 static bool ShouldBlockThread(void* aStartAddress) {
794 // Allows crashfirefox.exe to continue to work. Also if your threadproc is
795 // null, this crash is intentional.
796 if (aStartAddress == 0) return false;
797
798 bool shouldBlock = false;
799 MEMORY_BASIC_INFORMATION startAddressInfo = {0};
800 if (VirtualQuery(aStartAddress, &startAddressInfo,
801 sizeof(startAddressInfo))) {
802 shouldBlock |= startAddressInfo.State != MEM_COMMIT;
803 shouldBlock |= startAddressInfo.Protect != PAGE_EXECUTE_READ;
804 }
805
806 return shouldBlock;
807 }
808
809 // Allows blocked threads to still run normally through BaseThreadInitThunk, in
810 // case there's any magic there that we shouldn't skip.
NopThreadProc(void *)811 static DWORD WINAPI NopThreadProc(void* /* aThreadParam */) { return 0; }
812
patched_BaseThreadInitThunk(BOOL aIsInitialThread,void * aStartAddress,void * aThreadParam)813 static MOZ_NORETURN void __fastcall patched_BaseThreadInitThunk(
814 BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam) {
815 if (ShouldBlockThread(aStartAddress)) {
816 aStartAddress = (void*)NopThreadProc;
817 }
818
819 stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
820 }
821
822 static WindowsDllInterceptor NtDllIntercept;
823 static WindowsDllInterceptor Kernel32Intercept;
824
DllBlocklist_Initialize(uint32_t aInitFlags)825 MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) {
826 if (sBlocklistInitAttempted) {
827 return;
828 }
829 sInitFlags = aInitFlags;
830 sBlocklistInitAttempted = true;
831
832 // In order to be effective against AppInit DLLs, the blocklist must be
833 // initialized before user32.dll is loaded into the process (bug 932100).
834 if (GetModuleHandleA("user32.dll")) {
835 sUser32BeforeBlocklist = true;
836 #ifdef DEBUG
837 printf_stderr("DLL blocklist was unable to intercept AppInit DLLs.\n");
838 #endif
839 }
840
841 NtDllIntercept.Init("ntdll.dll");
842
843 ReentrancySentinel::InitializeStatics();
844
845 // We specifically use a detour, because there are cases where external
846 // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
847 // nop space patches. (Bug 951827)
848 bool ok = NtDllIntercept.AddDetour(
849 "LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll),
850 (void**)&stub_LdrLoadDll);
851
852 if (!ok) {
853 sBlocklistInitFailed = true;
854 #ifdef DEBUG
855 printf_stderr("LdrLoadDll hook failed, no dll blocklisting active\n");
856 #endif
857 }
858
859 // If someone injects a thread early that causes user32.dll to load off the
860 // main thread this causes issues, so load it as soon as we've initialized
861 // the block-list. (See bug 1400637)
862 if (!sUser32BeforeBlocklist) {
863 ::LoadLibraryW(L"user32.dll");
864 }
865
866 Kernel32Intercept.Init("kernel32.dll");
867
868 #ifdef _M_AMD64
869 if (!IsWin8OrLater()) {
870 // The crash that this hook works around is only seen on Win7.
871 Kernel32Intercept.AddHook(
872 "RtlInstallFunctionTableCallback",
873 reinterpret_cast<intptr_t>(patched_RtlInstallFunctionTableCallback),
874 (void**)&stub_RtlInstallFunctionTableCallback);
875 }
876 #endif
877
878 // Bug 1361410: WRusr.dll will overwrite our hook and cause a crash.
879 // Workaround: If we detect WRusr.dll, don't hook.
880 if (!GetModuleHandleW(L"WRusr.dll")) {
881 if (!Kernel32Intercept.AddDetour(
882 "BaseThreadInitThunk",
883 reinterpret_cast<intptr_t>(patched_BaseThreadInitThunk),
884 (void**)&stub_BaseThreadInitThunk)) {
885 #ifdef DEBUG
886 printf_stderr("BaseThreadInitThunk hook failed\n");
887 #endif
888 }
889 }
890 }
891
DllBlocklist_WriteNotes(HANDLE file)892 MFBT_API void DllBlocklist_WriteNotes(HANDLE file) {
893 DWORD nBytes;
894
895 WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes,
896 nullptr);
897 DllBlockSet::Write(file);
898 WriteFile(file, "\n", 1, &nBytes, nullptr);
899
900 if (sBlocklistInitFailed) {
901 WriteFile(file, kBlocklistInitFailedParameter,
902 kBlocklistInitFailedParameterLen, &nBytes, nullptr);
903 }
904
905 if (sUser32BeforeBlocklist) {
906 WriteFile(file, kUser32BeforeBlocklistParameter,
907 kUser32BeforeBlocklistParameterLen, &nBytes, nullptr);
908 }
909 }
910
DllBlocklist_CheckStatus()911 MFBT_API bool DllBlocklist_CheckStatus() {
912 if (sBlocklistInitFailed || sUser32BeforeBlocklist) return false;
913 return true;
914 }
915
916 // ============================================================================
917 // This section is for DLL Services
918 // ============================================================================
919
920 static SRWLOCK gDllServicesLock = SRWLOCK_INIT;
921 static mozilla::glue::detail::DllServicesBase* gDllServices;
922
923 class MOZ_RAII AutoSharedLock final {
924 public:
AutoSharedLock(SRWLOCK & aLock)925 explicit AutoSharedLock(SRWLOCK& aLock) : mLock(aLock) {
926 ::AcquireSRWLockShared(&aLock);
927 }
928
~AutoSharedLock()929 ~AutoSharedLock() { ::ReleaseSRWLockShared(&mLock); }
930
931 AutoSharedLock(const AutoSharedLock&) = delete;
932 AutoSharedLock(AutoSharedLock&&) = delete;
933 AutoSharedLock& operator=(const AutoSharedLock&) = delete;
934 AutoSharedLock& operator=(AutoSharedLock&&) = delete;
935
936 private:
937 SRWLOCK& mLock;
938 };
939
940 class MOZ_RAII AutoExclusiveLock final {
941 public:
AutoExclusiveLock(SRWLOCK & aLock)942 explicit AutoExclusiveLock(SRWLOCK& aLock) : mLock(aLock) {
943 ::AcquireSRWLockExclusive(&aLock);
944 }
945
~AutoExclusiveLock()946 ~AutoExclusiveLock() { ::ReleaseSRWLockExclusive(&mLock); }
947
948 AutoExclusiveLock(const AutoExclusiveLock&) = delete;
949 AutoExclusiveLock(AutoExclusiveLock&&) = delete;
950 AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
951 AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
952
953 private:
954 SRWLOCK& mLock;
955 };
956
957 // These types are documented on MSDN but not provided in any SDK headers
958
959 enum DllNotificationReason {
960 LDR_DLL_NOTIFICATION_REASON_LOADED = 1,
961 LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2
962 };
963
964 typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA {
965 ULONG Flags; // Reserved.
966 PCUNICODE_STRING FullDllName; // The full path name of the DLL module.
967 PCUNICODE_STRING BaseDllName; // The base file name of the DLL module.
968 PVOID DllBase; // A pointer to the base address for the DLL in memory.
969 ULONG SizeOfImage; // The size of the DLL image, in bytes.
970 } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA;
971
972 typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA {
973 ULONG Flags; // Reserved.
974 PCUNICODE_STRING FullDllName; // The full path name of the DLL module.
975 PCUNICODE_STRING BaseDllName; // The base file name of the DLL module.
976 PVOID DllBase; // A pointer to the base address for the DLL in memory.
977 ULONG SizeOfImage; // The size of the DLL image, in bytes.
978 } LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
979
980 typedef union _LDR_DLL_NOTIFICATION_DATA {
981 LDR_DLL_LOADED_NOTIFICATION_DATA Loaded;
982 LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded;
983 } LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA;
984
985 typedef const LDR_DLL_NOTIFICATION_DATA* PCLDR_DLL_NOTIFICATION_DATA;
986
987 typedef VOID(CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)(
988 ULONG aReason, PCLDR_DLL_NOTIFICATION_DATA aNotificationData,
989 PVOID aContext);
990
991 NTSTATUS NTAPI LdrRegisterDllNotification(
992 ULONG aFlags, PLDR_DLL_NOTIFICATION_FUNCTION aCallback, PVOID aContext,
993 PVOID* aCookie);
994
995 static PVOID gNotificationCookie;
996
DllLoadNotification(ULONG aReason,PCLDR_DLL_NOTIFICATION_DATA aNotificationData,PVOID aContext)997 static VOID CALLBACK DllLoadNotification(
998 ULONG aReason, PCLDR_DLL_NOTIFICATION_DATA aNotificationData,
999 PVOID aContext) {
1000 if (aReason != LDR_DLL_NOTIFICATION_REASON_LOADED) {
1001 // We don't care about unloads
1002 return;
1003 }
1004
1005 AutoSharedLock lock(gDllServicesLock);
1006 if (!gDllServices) {
1007 return;
1008 }
1009
1010 PCUNICODE_STRING fullDllName = aNotificationData->Loaded.FullDllName;
1011 gDllServices->DispatchDllLoadNotification(fullDllName);
1012 }
1013
1014 namespace mozilla {
1015 Authenticode* GetAuthenticode();
1016 } // namespace mozilla
1017
DllBlocklist_SetDllServices(mozilla::glue::detail::DllServicesBase * aSvc)1018 MFBT_API void DllBlocklist_SetDllServices(
1019 mozilla::glue::detail::DllServicesBase* aSvc) {
1020 AutoExclusiveLock lock(gDllServicesLock);
1021
1022 if (aSvc) {
1023 aSvc->SetAuthenticodeImpl(GetAuthenticode());
1024
1025 if (!gNotificationCookie) {
1026 auto pLdrRegisterDllNotification =
1027 reinterpret_cast<decltype(&::LdrRegisterDllNotification)>(
1028 ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"),
1029 "LdrRegisterDllNotification"));
1030
1031 MOZ_DIAGNOSTIC_ASSERT(pLdrRegisterDllNotification);
1032
1033 NTSTATUS ntStatus = pLdrRegisterDllNotification(
1034 0, &DllLoadNotification, nullptr, &gNotificationCookie);
1035 MOZ_DIAGNOSTIC_ASSERT(NT_SUCCESS(ntStatus));
1036 }
1037 }
1038
1039 gDllServices = aSvc;
1040 }
1041