1 /* -*- Mode: C++; tab-width: 2; 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 
8 #include "shared-libraries.h"
9 #include "nsWindowsHelpers.h"
10 #include "mozilla/NativeNt.h"
11 #include "mozilla/WindowsEnumProcessModules.h"
12 #include "mozilla/WindowsProcessMitigations.h"
13 #include "mozilla/WindowsVersion.h"
14 #include "nsPrintfCString.h"
15 
IsModuleUnsafeToLoad(const nsAString & aModuleName)16 static bool IsModuleUnsafeToLoad(const nsAString& aModuleName) {
17 #if defined(_M_AMD64) || defined(_M_IX86)
18   // Hackaround for Bug 1607574.  Nvidia's shim driver nvd3d9wrap[x].dll detours
19   // LoadLibraryExW and it causes AV when the following conditions are met.
20   //   1. LoadLibraryExW was called for "detoured.dll"
21   //   2. nvinit[x].dll was unloaded
22   //   3. OS version is older than 6.2
23 #  if defined(_M_AMD64)
24   LPCWSTR kNvidiaShimDriver = L"nvd3d9wrapx.dll";
25   LPCWSTR kNvidiaInitDriver = L"nvinitx.dll";
26 #  elif defined(_M_IX86)
27   LPCWSTR kNvidiaShimDriver = L"nvd3d9wrap.dll";
28   LPCWSTR kNvidiaInitDriver = L"nvinit.dll";
29 #  endif
30   if (aModuleName.LowerCaseEqualsLiteral("detoured.dll") &&
31       !mozilla::IsWin8OrLater() && ::GetModuleHandleW(kNvidiaShimDriver) &&
32       !::GetModuleHandleW(kNvidiaInitDriver)) {
33     return true;
34   }
35 #endif  // defined(_M_AMD64) || defined(_M_IX86)
36 
37   // Hackaround for Bug 1723868.  There is no safe way to prevent the module
38   // Microsoft's VP9 Video Decoder from being unloaded because mfplat.dll may
39   // have posted more than one task to unload the module in the work queue
40   // without calling LoadLibrary.
41   if (aModuleName.LowerCaseEqualsLiteral("msvp9dec_store.dll")) {
42     return true;
43   }
44 
45   return false;
46 }
47 
GetInfoForSelf()48 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() {
49   SharedLibraryInfo sharedLibraryInfo;
50 
51   auto addSharedLibraryFromModuleInfo = [&sharedLibraryInfo](
52                                             const wchar_t* aModulePath,
53                                             HMODULE aModule) {
54     nsDependentSubstring moduleNameStr(
55         mozilla::nt::GetLeafName(nsDependentString(aModulePath)));
56 
57     // If the module is unsafe to call LoadLibraryEx for, we skip.
58     if (IsModuleUnsafeToLoad(moduleNameStr)) {
59       return;
60     }
61 
62     // If EAF+ is enabled, parsing ntdll's PE header causes a crash.
63     if (mozilla::IsEafPlusEnabled() &&
64         moduleNameStr.LowerCaseEqualsLiteral("ntdll.dll")) {
65       return;
66     }
67 
68     // Load the module again to make sure that its handle will remain
69     // valid as we attempt to read the PDB information from it.  We load the
70     // DLL as a datafile so that we don't end up running the newly loaded
71     // module's DllMain function.  If the original handle |aModule| is valid,
72     // LoadLibraryEx just increments its refcount.
73     // LOAD_LIBRARY_AS_IMAGE_RESOURCE is needed to read information from the
74     // sections (not PE headers) which should be relocated by the loader,
75     // otherwise GetPdbInfo() will cause a crash.
76     nsModuleHandle handleLock(::LoadLibraryExW(
77         aModulePath, NULL,
78         LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE));
79     if (!handleLock) {
80       return;
81     }
82 
83     mozilla::nt::PEHeaders headers(handleLock.get());
84     if (!headers) {
85       return;
86     }
87 
88     mozilla::Maybe<mozilla::Range<const uint8_t>> bounds = headers.GetBounds();
89     if (!bounds) {
90       return;
91     }
92 
93     // Put the original |aModule| into SharedLibrary, but we get debug info
94     // from |handleLock| as |aModule| might be inaccessible.
95     const uintptr_t modStart = reinterpret_cast<uintptr_t>(aModule);
96     const uintptr_t modEnd = modStart + bounds->length();
97 
98     nsAutoCString breakpadId;
99     nsAutoString pdbPathStr;
100     if (const auto* debugInfo = headers.GetPdbInfo()) {
101       MOZ_ASSERT(breakpadId.IsEmpty());
102       const GUID& pdbSig = debugInfo->pdbSignature;
103       breakpadId.AppendPrintf(
104           "%08X"                              // m0
105           "%04X%04X"                          // m1,m2
106           "%02X%02X%02X%02X%02X%02X%02X%02X"  // m3
107           "%X",                               // pdbAge
108           pdbSig.Data1, pdbSig.Data2, pdbSig.Data3, pdbSig.Data4[0],
109           pdbSig.Data4[1], pdbSig.Data4[2], pdbSig.Data4[3], pdbSig.Data4[4],
110           pdbSig.Data4[5], pdbSig.Data4[6], pdbSig.Data4[7], debugInfo->pdbAge);
111 
112       // The PDB file name could be different from module filename,
113       // so report both
114       // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
115       pdbPathStr = NS_ConvertUTF8toUTF16(debugInfo->pdbFileName);
116     }
117 
118     nsAutoCString versionStr;
119     uint64_t version;
120     if (headers.GetVersionInfo(version)) {
121       versionStr.AppendPrintf("%u.%u.%u.%u",
122                               static_cast<uint32_t>((version >> 48) & 0xFFFFu),
123                               static_cast<uint32_t>((version >> 32) & 0xFFFFu),
124                               static_cast<uint32_t>((version >> 16) & 0xFFFFu),
125                               static_cast<uint32_t>(version & 0xFFFFu));
126     }
127 
128     const nsString& pdbNameStr =
129         PromiseFlatString(mozilla::nt::GetLeafName(pdbPathStr));
130     SharedLibrary shlib(modStart, modEnd,
131                         0,  // DLLs are always mapped at offset 0 on Windows
132                         breakpadId, PromiseFlatString(moduleNameStr),
133                         nsDependentString(aModulePath), pdbNameStr, pdbPathStr,
134                         versionStr, "");
135     sharedLibraryInfo.AddSharedLibrary(shlib);
136   };
137 
138   mozilla::EnumerateProcessModules(addSharedLibraryFromModuleInfo);
139   return sharedLibraryInfo;
140 }
141 
Initialize()142 void SharedLibraryInfo::Initialize() { /* do nothing */
143 }
144