tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */ }