shared-libraries-win32.cc (8045B)
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 "mozilla/SharedLibraries.h" 9 10 #include "mozilla/glue/WindowsUnicode.h" 11 #include "mozilla/NativeNt.h" 12 #include "mozilla/WindowsEnumProcessModules.h" 13 #include "mozilla/WindowsProcessMitigations.h" 14 15 #include <cctype> 16 #include <string> 17 18 static constexpr char uppercaseDigits[16] = {'0', '1', '2', '3', '4', '5', 19 '6', '7', '8', '9', 'A', 'B', 20 'C', 'D', 'E', 'F'}; 21 static constexpr char lowercaseDigits[16] = {'0', '1', '2', '3', '4', '5', 22 '6', '7', '8', '9', 'a', 'b', 23 'c', 'd', 'e', 'f'}; 24 25 static void AppendHex(const unsigned char* aBegin, const unsigned char* aEnd, 26 std::string& aOut) { 27 for (const unsigned char* p = aBegin; p < aEnd; ++p) { 28 unsigned char c = *p; 29 aOut += uppercaseDigits[c >> 4]; 30 aOut += uppercaseDigits[c & 0xFu]; 31 } 32 } 33 34 static constexpr bool WITH_PADDING = true; 35 static constexpr bool WITHOUT_PADDING = false; 36 static constexpr bool LOWERCASE = true; 37 static constexpr bool UPPERCASE = false; 38 template <typename T> 39 static void AppendHex(T aValue, std::string& aOut, bool aWithPadding, 40 bool aLowercase = UPPERCASE) { 41 for (int i = sizeof(T) * 2 - 1; i >= 0; --i) { 42 unsigned nibble = (aValue >> (i * 4)) & 0xFu; 43 // If no-padding requested, skip starting zeroes -- unless we're on the very 44 // last nibble (so we don't output a blank). 45 if (!aWithPadding && i != 0) { 46 if (nibble == 0) { 47 // Requested no padding, skip zeroes. 48 continue; 49 } 50 // Requested no padding, got first non-zero, pretend we now want padding 51 // so we don't skip zeroes anymore. 52 aWithPadding = true; 53 } 54 aOut += aLowercase ? lowercaseDigits[nibble] : uppercaseDigits[nibble]; 55 } 56 } 57 58 bool LowerCaseEqualsLiteral(char aModuleChar, char aDetouredChar) { 59 return std::tolower(aModuleChar) == aDetouredChar; 60 } 61 62 static bool IsModuleUnsafeToLoad(const std::string& aModuleName) { 63 // Hackaround for Bug 1723868. There is no safe way to prevent the module 64 // Microsoft's VP9 Video Decoder from being unloaded because mfplat.dll may 65 // have posted more than one task to unload the module in the work queue 66 // without calling LoadLibrary. 67 constexpr std::string_view vp9_decoder_dll = "msvp9dec_store.dll"; 68 if (std::equal(aModuleName.cbegin(), aModuleName.cend(), 69 vp9_decoder_dll.cbegin(), vp9_decoder_dll.cend(), 70 LowerCaseEqualsLiteral)) { 71 return true; 72 } 73 74 return false; 75 } 76 77 void AddSharedLibraryFromModuleInfo(SharedLibraryInfo& sharedLibraryInfo, 78 const wchar_t* aModulePath, 79 mozilla::Maybe<HMODULE> aModule) { 80 mozilla::UniquePtr<char[]> utf8ModulePath( 81 mozilla::glue::WideToUTF8(aModulePath)); 82 if (!utf8ModulePath) { 83 return; 84 } 85 86 std::string modulePathStr(utf8ModulePath.get()); 87 size_t pos = modulePathStr.find_last_of("\\/"); 88 std::string moduleNameStr = (pos != std::string::npos) 89 ? modulePathStr.substr(pos + 1) 90 : modulePathStr; 91 92 // If the module is unsafe to call LoadLibraryEx for, we skip. 93 if (IsModuleUnsafeToLoad(moduleNameStr)) { 94 return; 95 } 96 97 // If EAF+ is enabled, parsing ntdll's PE header causes a crash. 98 constexpr std::string_view ntdll_dll = "ntdll.dll"; 99 if (mozilla::IsEafPlusEnabled() && 100 std::equal(moduleNameStr.cbegin(), moduleNameStr.cend(), 101 ntdll_dll.cbegin(), ntdll_dll.cend(), 102 LowerCaseEqualsLiteral)) { 103 return; 104 } 105 106 // Load the module again - to make sure that its handle will remain valid as 107 // we attempt to read the PDB information from it - or for the first time if 108 // we only have a path. We want to load the DLL without running the newly 109 // loaded module's DllMain function, but not as a data file because we want 110 // to be able to do RVA computations easily. Hence, we use the flag 111 // LOAD_LIBRARY_AS_IMAGE_RESOURCE which ensures that the sections (not PE 112 // headers) will be relocated by the loader. Otherwise GetPdbInfo() and/or 113 // GetVersionInfo() can cause a crash. If the original handle |aModule| is 114 // valid, LoadLibraryEx just increments its refcount. 115 nsModuleHandle handleLock( 116 ::LoadLibraryExW(aModulePath, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE)); 117 if (!handleLock) { 118 return; 119 } 120 121 mozilla::nt::PEHeaders headers(handleLock.get()); 122 if (!headers) { 123 return; 124 } 125 126 mozilla::Maybe<mozilla::Range<const uint8_t>> bounds = headers.GetBounds(); 127 if (!bounds) { 128 return; 129 } 130 131 // Put the original |aModule| into SharedLibrary, but we get debug info 132 // from |handleLock| as |aModule| might be inaccessible. 133 const uintptr_t modStart = 134 aModule.isSome() ? reinterpret_cast<uintptr_t>(*aModule) 135 : reinterpret_cast<uintptr_t>(handleLock.get()); 136 const uintptr_t modEnd = modStart + bounds->length(); 137 138 std::string breakpadId; 139 std::string pdbPathStr; 140 std::string pdbNameStr; 141 if (const auto* debugInfo = headers.GetPdbInfo()) { 142 MOZ_ASSERT(breakpadId.empty()); 143 const GUID& pdbSig = debugInfo->pdbSignature; 144 AppendHex(pdbSig.Data1, breakpadId, WITH_PADDING); 145 AppendHex(pdbSig.Data2, breakpadId, WITH_PADDING); 146 AppendHex(pdbSig.Data3, breakpadId, WITH_PADDING); 147 AppendHex(reinterpret_cast<const unsigned char*>(&pdbSig.Data4), 148 reinterpret_cast<const unsigned char*>(&pdbSig.Data4) + 149 sizeof(pdbSig.Data4), 150 breakpadId); 151 AppendHex(debugInfo->pdbAge, breakpadId, WITHOUT_PADDING); 152 153 // The PDB file name could be different from module filename, 154 // so report both 155 // e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb 156 pdbPathStr = debugInfo->pdbFileName; 157 size_t pos = pdbPathStr.find_last_of("\\/"); 158 pdbNameStr = 159 (pos != std::string::npos) ? pdbPathStr.substr(pos + 1) : pdbPathStr; 160 } 161 162 std::string codeId; 163 DWORD timestamp; 164 DWORD imageSize; 165 if (headers.GetTimeStamp(timestamp) && headers.GetImageSize(imageSize)) { 166 AppendHex(timestamp, codeId, WITH_PADDING); 167 AppendHex(imageSize, codeId, WITHOUT_PADDING, LOWERCASE); 168 } 169 170 std::string versionStr; 171 uint64_t version; 172 if (headers.GetVersionInfo(version)) { 173 versionStr += std::to_string((version >> 48) & 0xFFFF); 174 versionStr += '.'; 175 versionStr += std::to_string((version >> 32) & 0xFFFF); 176 versionStr += '.'; 177 versionStr += std::to_string((version >> 16) & 0xFFFF); 178 versionStr += '.'; 179 versionStr += std::to_string(version & 0xFFFF); 180 } 181 182 SharedLibrary shlib(modStart, modEnd, 183 0, // DLLs are always mapped at offset 0 on Windows 184 breakpadId, codeId, moduleNameStr, modulePathStr, 185 pdbNameStr, pdbPathStr, versionStr, ""); 186 sharedLibraryInfo.AddSharedLibrary(shlib); 187 } 188 189 SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() { 190 SharedLibraryInfo sharedLibraryInfo; 191 192 auto addSharedLibraryFromModuleInfo = 193 [&sharedLibraryInfo](const wchar_t* aModulePath, HMODULE aModule) { 194 AddSharedLibraryFromModuleInfo(sharedLibraryInfo, aModulePath, 195 mozilla::Some(aModule)); 196 }; 197 198 mozilla::EnumerateProcessModules(addSharedLibraryFromModuleInfo); 199 return sharedLibraryInfo; 200 } 201 202 SharedLibraryInfo SharedLibraryInfo::GetInfoFromPath(const wchar_t* aPath) { 203 SharedLibraryInfo sharedLibraryInfo; 204 AddSharedLibraryFromModuleInfo(sharedLibraryInfo, aPath, mozilla::Nothing()); 205 return sharedLibraryInfo; 206 } 207 208 void SharedLibraryInfo::Initialize() { /* do nothing */ }