mozglue/build/WindowsDllBlocklist.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:38daff775522
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 #include <windows.h>
7 #include <winternl.h>
8 #include <io.h>
9
10 #pragma warning( push )
11 #pragma warning( disable : 4275 4530 ) // See msvc-stl-wrapper.template.h
12 #include <map>
13 #pragma warning( pop )
14
15 #define MOZ_NO_MOZALLOC
16 #include "nsAutoPtr.h"
17
18 #include "nsWindowsDllInterceptor.h"
19 #include "mozilla/WindowsVersion.h"
20 #include "nsWindowsHelpers.h"
21
22 using namespace mozilla;
23
24 #define ALL_VERSIONS ((unsigned long long)-1LL)
25
26 // DLLs sometimes ship without a version number, particularly early
27 // releases. Blocking "version <= 0" has the effect of blocking unversioned
28 // DLLs (since the call to get version info fails), but not blocking
29 // any versioned instance.
30 #define UNVERSIONED ((unsigned long long)0LL)
31
32 // Convert the 4 (decimal) components of a DLL version number into a
33 // single unsigned long long, as needed by the blocklist
34 #define MAKE_VERSION(a,b,c,d)\
35 ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL)
36
37 struct DllBlockInfo {
38 // The name of the DLL -- in LOWERCASE! It will be compared to
39 // a lowercase version of the DLL name only.
40 const char *name;
41
42 // If maxVersion is ALL_VERSIONS, we'll block all versions of this
43 // dll. Otherwise, we'll block all versions less than or equal to
44 // the given version, as queried by GetFileVersionInfo and
45 // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields.
46 //
47 // Note that the version is usually 4 components, which is A.B.C.D
48 // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity),
49 // but it's not required to be of that format.
50 //
51 // If the USE_TIMESTAMP flag is set, then we use the timestamp from
52 // the IMAGE_FILE_HEADER in lieu of a version number.
53 unsigned long long maxVersion;
54
55 enum {
56 FLAGS_DEFAULT = 0,
57 BLOCK_WIN8PLUS_ONLY = 1,
58 BLOCK_XP_ONLY = 2,
59 USE_TIMESTAMP = 4,
60 } flags;
61 };
62
63 static DllBlockInfo sWindowsDllBlocklist[] = {
64 // EXAMPLE:
65 // { "uxtheme.dll", ALL_VERSIONS },
66 // { "uxtheme.dll", 0x0000123400000000ULL },
67 // The DLL name must be in lowercase!
68
69 // NPFFAddon - Known malware
70 { "npffaddon.dll", ALL_VERSIONS},
71
72 // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted
73 {"avgrsstx.dll", MAKE_VERSION(8,5,0,401)},
74
75 // calc.dll - Suspected malware
76 {"calc.dll", MAKE_VERSION(1,0,0,1)},
77
78 // hook.dll - Suspected malware
79 {"hook.dll", ALL_VERSIONS},
80
81 // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances
82 // of this DLL cause crashes
83 {"googledesktopnetwork3.dll", UNVERSIONED},
84
85 // rdolib.dll - Suspected malware
86 {"rdolib.dll", MAKE_VERSION(6,0,88,4)},
87
88 // fgjk4wvb.dll - Suspected malware
89 {"fgjk4wvb.dll", MAKE_VERSION(8,8,8,8)},
90
91 // radhslib.dll - Naomi internet filter - unmaintained since 2006
92 {"radhslib.dll", UNVERSIONED},
93
94 // Music download filter for vkontakte.ru - old instances
95 // of this DLL cause crashes
96 {"vksaver.dll", MAKE_VERSION(2,2,2,0)},
97
98 // Topcrash in Firefox 4.0b1
99 {"rlxf.dll", MAKE_VERSION(1,2,323,1)},
100
101 // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox
102 // Adobe photoshop library, now redundant in later installations
103 {"psicon.dll", ALL_VERSIONS},
104
105 // Topcrash in Firefox 4 betas (bug 618899)
106 {"accelerator.dll", MAKE_VERSION(3,2,1,6)},
107
108 // Topcrash with Roboform in Firefox 8 (bug 699134)
109 {"rf-firefox.dll", MAKE_VERSION(7,6,1,0)},
110 {"roboform.dll", MAKE_VERSION(7,6,1,0)},
111
112 // Topcrash with Babylon Toolbar on FF16+ (bug 721264)
113 {"babyfox.dll", ALL_VERSIONS},
114
115 // sprotector.dll crashes, bug 957258
116 {"sprotector.dll", ALL_VERSIONS},
117
118 // Topcrash with Websense Endpoint, bug 828184
119 {"qipcap.dll", MAKE_VERSION(7, 6, 815, 1)},
120
121 // leave these two in always for tests
122 { "mozdllblockingtest.dll", ALL_VERSIONS },
123 { "mozdllblockingtest_versioned.dll", 0x0000000400000000ULL },
124
125 // Windows Media Foundation FLAC decoder and type sniffer (bug 839031).
126 { "mfflac.dll", ALL_VERSIONS },
127
128 // Older Relevant Knowledge DLLs cause us to crash (bug 904001).
129 { "rlnx.dll", MAKE_VERSION(1, 3, 334, 9) },
130 { "pmnx.dll", MAKE_VERSION(1, 3, 334, 9) },
131 { "opnx.dll", MAKE_VERSION(1, 3, 334, 9) },
132 { "prnx.dll", MAKE_VERSION(1, 3, 334, 9) },
133
134 // Older belgian ID card software causes Firefox to crash or hang on
135 // shutdown, bug 831285 and 918399.
136 { "beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968) },
137
138 // bug 925459, bitguard crashes
139 { "bitguard.dll", ALL_VERSIONS },
140
141 // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed
142 // Software is discontinued/unsupported
143 { "atkdx11disp.dll", ALL_VERSIONS },
144
145 // Topcrash with Conduit SearchProtect, bug 944542
146 { "spvc32.dll", ALL_VERSIONS },
147
148 // XP topcrash with F-Secure, bug 970362
149 { "fs_ccf_ni_umh32.dll", MAKE_VERSION(1, 42, 101, 0), DllBlockInfo::BLOCK_XP_ONLY },
150
151 // Topcrash with V-bates, bug 1002748 and bug 1023239
152 { "libinject.dll", UNVERSIONED },
153 { "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP },
154 { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP },
155
156 // Crashes with RoboForm2Go written against old SDK, bug 988311
157 { "rf-firefox-22.dll", ALL_VERSIONS },
158
159 { nullptr, 0 }
160 };
161
162 #ifndef STATUS_DLL_NOT_FOUND
163 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
164 #endif
165
166 // define this for very verbose dll load debug spew
167 #undef DEBUG_very_verbose
168
169 static const char kBlockedDllsParameter[] = "BlockedDllList=";
170 static const int kBlockedDllsParameterLen =
171 sizeof(kBlockedDllsParameter) - 1;
172
173 static const char kBlocklistInitFailedParameter[] = "BlocklistInitFailed=1\n";
174 static const int kBlocklistInitFailedParameterLen =
175 sizeof(kBlocklistInitFailedParameter) - 1;
176
177 static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n";
178 static const int kUser32BeforeBlocklistParameterLen =
179 sizeof(kUser32BeforeBlocklistParameter) - 1;
180
181 static DWORD sThreadLoadingXPCOMModule;
182 static bool sBlocklistInitFailed;
183 static bool sUser32BeforeBlocklist;
184
185 // Duplicated from xpcom glue. Ideally this should be shared.
186 void
187 printf_stderr(const char *fmt, ...)
188 {
189 if (IsDebuggerPresent()) {
190 char buf[2048];
191 va_list args;
192 va_start(args, fmt);
193 vsnprintf(buf, sizeof(buf), fmt, args);
194 buf[sizeof(buf) - 1] = '\0';
195 va_end(args);
196 OutputDebugStringA(buf);
197 }
198
199 FILE *fp = _fdopen(_dup(2), "a");
200 if (!fp)
201 return;
202
203 va_list args;
204 va_start(args, fmt);
205 vfprintf(fp, fmt, args);
206 va_end(args);
207
208 fclose(fp);
209 }
210
211 namespace {
212
213 typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
214
215 static LdrLoadDll_func stub_LdrLoadDll = 0;
216
217 template <class T>
218 struct RVAMap {
219 RVAMap(HANDLE map, DWORD offset) {
220 SYSTEM_INFO info;
221 GetSystemInfo(&info);
222
223 DWORD alignedOffset = (offset / info.dwAllocationGranularity) *
224 info.dwAllocationGranularity;
225
226 MOZ_ASSERT(offset - alignedOffset < info.dwAllocationGranularity, "Wtf");
227
228 mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset,
229 sizeof(T) + (offset - alignedOffset));
230
231 mMappedView = mRealView ? reinterpret_cast<T*>((char*)mRealView + (offset - alignedOffset)) :
232 nullptr;
233 }
234 ~RVAMap() {
235 if (mRealView) {
236 ::UnmapViewOfFile(mRealView);
237 }
238 }
239 operator const T*() const { return mMappedView; }
240 const T* operator->() const { return mMappedView; }
241 private:
242 const T* mMappedView;
243 void* mRealView;
244 };
245
246 bool
247 CheckASLR(const wchar_t* path)
248 {
249 bool retval = false;
250
251 HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
252 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
253 nullptr);
254 if (file != INVALID_HANDLE_VALUE) {
255 HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0,
256 nullptr);
257 if (map) {
258 RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
259 if (peHeader) {
260 RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
261 if (ntHeader) {
262 // If the DLL has no code, permit it regardless of ASLR status.
263 if (ntHeader->OptionalHeader.SizeOfCode == 0) {
264 retval = true;
265 }
266 // Check to see if the DLL supports ASLR
267 else if ((ntHeader->OptionalHeader.DllCharacteristics &
268 IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) {
269 retval = true;
270 }
271 }
272 }
273 ::CloseHandle(map);
274 }
275 ::CloseHandle(file);
276 }
277
278 return retval;
279 }
280
281 DWORD
282 GetTimestamp(const wchar_t* path)
283 {
284 DWORD timestamp = 0;
285
286 HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
287 nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
288 nullptr);
289 if (file != INVALID_HANDLE_VALUE) {
290 HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0,
291 nullptr);
292 if (map) {
293 RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
294 if (peHeader) {
295 RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
296 if (ntHeader) {
297 timestamp = ntHeader->FileHeader.TimeDateStamp;
298 }
299 }
300 ::CloseHandle(map);
301 }
302 ::CloseHandle(file);
303 }
304
305 return timestamp;
306 }
307
308 // This lock protects both the reentrancy sentinel and the crash reporter
309 // data structures.
310 static CRITICAL_SECTION sLock;
311
312 /**
313 * Some versions of Windows call LoadLibraryEx to get the version information
314 * for a DLL, which causes our patched LdrLoadDll implementation to re-enter
315 * itself and cause infinite recursion and a stack-exhaustion crash. We protect
316 * against reentrancy by allowing recursive loads of the same DLL.
317 *
318 * Note that we don't use __declspec(thread) because that doesn't work in DLLs
319 * loaded via LoadLibrary and there can be a limited number of TLS slots, so
320 * we roll our own.
321 */
322 class ReentrancySentinel
323 {
324 public:
325 explicit ReentrancySentinel(const char* dllName)
326 {
327 DWORD currentThreadId = GetCurrentThreadId();
328 AutoCriticalSection lock(&sLock);
329 mPreviousDllName = (*sThreadMap)[currentThreadId];
330
331 // If there is a DLL currently being loaded and it has the same name
332 // as the current attempt, we're re-entering.
333 mReentered = mPreviousDllName && !stricmp(mPreviousDllName, dllName);
334 (*sThreadMap)[currentThreadId] = dllName;
335 }
336
337 ~ReentrancySentinel()
338 {
339 DWORD currentThreadId = GetCurrentThreadId();
340 AutoCriticalSection lock(&sLock);
341 (*sThreadMap)[currentThreadId] = mPreviousDllName;
342 }
343
344 bool BailOut() const
345 {
346 return mReentered;
347 };
348
349 static void InitializeStatics()
350 {
351 InitializeCriticalSection(&sLock);
352 sThreadMap = new std::map<DWORD, const char*>;
353 }
354
355 private:
356 static std::map<DWORD, const char*>* sThreadMap;
357
358 const char* mPreviousDllName;
359 bool mReentered;
360 };
361
362 std::map<DWORD, const char*>* ReentrancySentinel::sThreadMap;
363
364 /**
365 * This is a linked list of DLLs that have been blocked. It doesn't use
366 * mozilla::LinkedList because this is an append-only list and doesn't need
367 * to be doubly linked.
368 */
369 class DllBlockSet
370 {
371 public:
372 static void Add(const char* name, unsigned long long version);
373
374 // Write the list of blocked DLLs to a file HANDLE. This method is run after
375 // a crash occurs and must therefore not use the heap, etc.
376 static void Write(HANDLE file);
377
378 private:
379 DllBlockSet(const char* name, unsigned long long version)
380 : mName(name)
381 , mVersion(version)
382 , mNext(nullptr)
383 {
384 }
385
386 const char* mName; // points into the sWindowsDllBlocklist string
387 unsigned long long mVersion;
388 DllBlockSet* mNext;
389
390 static DllBlockSet* gFirst;
391 };
392
393 DllBlockSet* DllBlockSet::gFirst;
394
395 void
396 DllBlockSet::Add(const char* name, unsigned long long version)
397 {
398 AutoCriticalSection lock(&sLock);
399 for (DllBlockSet* b = gFirst; b; b = b->mNext) {
400 if (0 == strcmp(b->mName, name) && b->mVersion == version) {
401 return;
402 }
403 }
404 // Not already present
405 DllBlockSet* n = new DllBlockSet(name, version);
406 n->mNext = gFirst;
407 gFirst = n;
408 }
409
410 void
411 DllBlockSet::Write(HANDLE file)
412 {
413 AutoCriticalSection lock(&sLock);
414 DWORD nBytes;
415
416 // Because this method is called after a crash occurs, and uses heap memory,
417 // protect this entire block with a structured exception handler.
418 MOZ_SEH_TRY {
419 for (DllBlockSet* b = gFirst; b; b = b->mNext) {
420 // write name[,v.v.v.v];
421 WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
422 if (b->mVersion != -1) {
423 WriteFile(file, ",", 1, &nBytes, nullptr);
424 uint16_t parts[4];
425 parts[0] = b->mVersion >> 48;
426 parts[1] = (b->mVersion >> 32) & 0xFFFF;
427 parts[2] = (b->mVersion >> 16) & 0xFFFF;
428 parts[3] = b->mVersion & 0xFFFF;
429 for (int p = 0; p < 4; ++p) {
430 char buf[32];
431 ltoa(parts[p], buf, 10);
432 WriteFile(file, buf, strlen(buf), &nBytes, nullptr);
433 if (p != 3) {
434 WriteFile(file, ".", 1, &nBytes, nullptr);
435 }
436 }
437 }
438 WriteFile(file, ";", 1, &nBytes, nullptr);
439 }
440 }
441 MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { }
442 }
443
444 static
445 wchar_t* getFullPath (PWCHAR filePath, wchar_t* fname)
446 {
447 // In Windows 8, the first parameter seems to be used for more than just the
448 // path name. For example, its numerical value can be 1. Passing a non-valid
449 // pointer to SearchPathW will cause a crash, so we need to check to see if we
450 // are handed a valid pointer, and otherwise just pass nullptr to SearchPathW.
451 PWCHAR sanitizedFilePath = (intptr_t(filePath) < 1024) ? nullptr : filePath;
452
453 // figure out the length of the string that we need
454 DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, nullptr,
455 nullptr);
456 if (pathlen == 0) {
457 return nullptr;
458 }
459
460 wchar_t* full_fname = new wchar_t[pathlen+1];
461 if (!full_fname) {
462 // couldn't allocate memory?
463 return nullptr;
464 }
465
466 // now actually grab it
467 SearchPathW(sanitizedFilePath, fname, L".dll", pathlen + 1, full_fname,
468 nullptr);
469 return full_fname;
470 }
471
472 // No builtin function to find the last character matching a set
473 static wchar_t* lastslash(wchar_t* s, int len)
474 {
475 for (wchar_t* c = s + len - 1; c >= s; --c) {
476 if (*c == L'\\' || *c == L'/') {
477 return c;
478 }
479 }
480 return nullptr;
481 }
482
483 static NTSTATUS NTAPI
484 patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
485 {
486 // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename portion
487 #define DLLNAME_MAX 128
488 char dllName[DLLNAME_MAX+1];
489 wchar_t *dll_part;
490 char *dot;
491 DllBlockInfo *info;
492
493 int len = moduleFileName->Length / 2;
494 wchar_t *fname = moduleFileName->Buffer;
495 nsAutoArrayPtr<wchar_t> full_fname;
496
497 // The filename isn't guaranteed to be null terminated, but in practice
498 // it always will be; ensure that this is so, and bail if not.
499 // This is done instead of the more robust approach because of bug 527122,
500 // where lots of weird things were happening when we tried to make a copy.
501 if (moduleFileName->MaximumLength < moduleFileName->Length+2 ||
502 fname[len] != 0)
503 {
504 #ifdef DEBUG
505 printf_stderr("LdrLoadDll: non-null terminated string found!\n");
506 #endif
507 goto continue_loading;
508 }
509
510 dll_part = lastslash(fname, len);
511 if (dll_part) {
512 dll_part = dll_part + 1;
513 len -= dll_part - fname;
514 } else {
515 dll_part = fname;
516 }
517
518 #ifdef DEBUG_very_verbose
519 printf_stderr("LdrLoadDll: dll_part '%S' %d\n", dll_part, len);
520 #endif
521
522 // if it's too long, then, we assume we won't want to block it,
523 // since DLLNAME_MAX should be at least long enough to hold the longest
524 // entry in our blocklist.
525 if (len > DLLNAME_MAX) {
526 #ifdef DEBUG
527 printf_stderr("LdrLoadDll: len too long! %d\n", len);
528 #endif
529 goto continue_loading;
530 }
531
532 // copy over to our char byte buffer, lowercasing ASCII as we go
533 for (int i = 0; i < len; i++) {
534 wchar_t c = dll_part[i];
535
536 if (c > 0x7f) {
537 // welp, it's not ascii; if we need to add non-ascii things to
538 // our blocklist, we'll have to remove this limitation.
539 goto continue_loading;
540 }
541
542 // ensure that dll name is all lowercase
543 if (c >= 'A' && c <= 'Z')
544 c += 'a' - 'A';
545
546 dllName[i] = (char) c;
547 }
548
549 dllName[len] = 0;
550
551 #ifdef DEBUG_very_verbose
552 printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
553 #endif
554
555 // Block a suspicious binary that uses various 12-digit hex strings
556 // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
557 dot = strchr(dllName, '.');
558 if (dot && (strchr(dot+1, '.') == dot+13)) {
559 char * end = nullptr;
560 _strtoui64(dot+1, &end, 16);
561 if (end == dot+13) {
562 return STATUS_DLL_NOT_FOUND;
563 }
564 }
565
566 // then compare to everything on the blocklist
567 info = &sWindowsDllBlocklist[0];
568 while (info->name) {
569 if (strcmp(info->name, dllName) == 0)
570 break;
571
572 info++;
573 }
574
575 if (info->name) {
576 bool load_ok = false;
577
578 #ifdef DEBUG_very_verbose
579 printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
580 #endif
581
582 if ((info->flags == DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
583 !IsWin8OrLater()) {
584 goto continue_loading;
585 }
586
587 if ((info->flags == DllBlockInfo::BLOCK_XP_ONLY) &&
588 IsWin2003OrLater()) {
589 goto continue_loading;
590 }
591
592 unsigned long long fVersion = ALL_VERSIONS;
593
594 if (info->maxVersion != ALL_VERSIONS) {
595 ReentrancySentinel sentinel(dllName);
596 if (sentinel.BailOut()) {
597 goto continue_loading;
598 }
599
600 full_fname = getFullPath(filePath, fname);
601 if (!full_fname) {
602 // uh, we couldn't find the DLL at all, so...
603 printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
604 return STATUS_DLL_NOT_FOUND;
605 }
606
607 if (info->flags & DllBlockInfo::USE_TIMESTAMP) {
608 fVersion = GetTimestamp(full_fname);
609 if (fVersion > info->maxVersion) {
610 load_ok = true;
611 }
612 } else {
613 DWORD zero;
614 DWORD infoSize = GetFileVersionInfoSizeW(full_fname, &zero);
615
616 // If we failed to get the version information, we block.
617
618 if (infoSize != 0) {
619 nsAutoArrayPtr<unsigned char> infoData(new unsigned char[infoSize]);
620 VS_FIXEDFILEINFO *vInfo;
621 UINT vInfoLen;
622
623 if (GetFileVersionInfoW(full_fname, 0, infoSize, infoData) &&
624 VerQueryValueW(infoData, L"\\", (LPVOID*) &vInfo, &vInfoLen))
625 {
626 fVersion =
627 ((unsigned long long)vInfo->dwFileVersionMS) << 32 |
628 ((unsigned long long)vInfo->dwFileVersionLS);
629
630 // finally do the version check, and if it's greater than our block
631 // version, keep loading
632 if (fVersion > info->maxVersion)
633 load_ok = true;
634 }
635 }
636 }
637 }
638
639 if (!load_ok) {
640 printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
641 DllBlockSet::Add(info->name, fVersion);
642 return STATUS_DLL_NOT_FOUND;
643 }
644 }
645
646 continue_loading:
647 #ifdef DEBUG_very_verbose
648 printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
649 #endif
650
651 if (GetCurrentThreadId() == sThreadLoadingXPCOMModule) {
652 // Check to ensure that the DLL has ASLR.
653 full_fname = getFullPath(filePath, fname);
654 if (!full_fname) {
655 // uh, we couldn't find the DLL at all, so...
656 printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
657 return STATUS_DLL_NOT_FOUND;
658 }
659
660 if (IsVistaOrLater() && !CheckASLR(full_fname)) {
661 printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName);
662 return STATUS_DLL_NOT_FOUND;
663 }
664 }
665
666 return stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
667 }
668
669 WindowsDllInterceptor NtDllIntercept;
670
671 } // anonymous namespace
672
673 NS_EXPORT void
674 DllBlocklist_Initialize()
675 {
676 if (GetModuleHandleA("user32.dll")) {
677 sUser32BeforeBlocklist = true;
678 }
679
680 NtDllIntercept.Init("ntdll.dll");
681
682 ReentrancySentinel::InitializeStatics();
683
684 // We specifically use a detour, because there are cases where external
685 // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
686 // nop space patches. (Bug 951827)
687 bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll);
688
689 if (!ok) {
690 sBlocklistInitFailed = true;
691 #ifdef DEBUG
692 printf_stderr ("LdrLoadDll hook failed, no dll blocklisting active\n");
693 #endif
694 }
695 }
696
697 NS_EXPORT void
698 DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread)
699 {
700 if (inXPCOMLoadOnMainThread) {
701 MOZ_ASSERT(sThreadLoadingXPCOMModule == 0, "Only one thread should be doing this");
702 sThreadLoadingXPCOMModule = GetCurrentThreadId();
703 } else {
704 sThreadLoadingXPCOMModule = 0;
705 }
706 }
707
708 NS_EXPORT void
709 DllBlocklist_WriteNotes(HANDLE file)
710 {
711 DWORD nBytes;
712
713 WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
714 DllBlockSet::Write(file);
715 WriteFile(file, "\n", 1, &nBytes, nullptr);
716
717 if (sBlocklistInitFailed) {
718 WriteFile(file, kBlocklistInitFailedParameter,
719 kBlocklistInitFailedParameterLen, &nBytes, nullptr);
720 }
721
722 if (sUser32BeforeBlocklist) {
723 WriteFile(file, kUser32BeforeBlocklistParameter,
724 kUser32BeforeBlocklistParameterLen, &nBytes, nullptr);
725 }
726 }

mercurial