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