mozglue/build/WindowsDllBlocklist.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial