tor-browser

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

LoaderPrivateAPI.cpp (10472B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #include "LoaderPrivateAPI.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 #include "../DllBlocklistInit.h"
     11 #include "../ErrorHandler.h"
     12 #include "SharedSection.h"
     13 
     14 using GlobalInitializerFn = void(__cdecl*)(void);
     15 
     16 // Allocation of static initialization section for the freestanding library
     17 #pragma section(".freestd$a", read)
     18 __declspec(allocate(
     19    ".freestd$a")) static const GlobalInitializerFn FreeStdStart =
     20    reinterpret_cast<GlobalInitializerFn>(0);
     21 
     22 #pragma section(".freestd$z", read)
     23 __declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd =
     24    reinterpret_cast<GlobalInitializerFn>(0);
     25 
     26 namespace mozilla {
     27 namespace freestanding {
     28 
     29 static RTL_RUN_ONCE gRunOnce = RTL_RUN_ONCE_INIT;
     30 
     31 // The contract for this callback is identical to the InitOnceCallback from
     32 // Win32 land; we're just using ntdll-layer types instead.
     33 static ULONG NTAPI DoOneTimeInit(PRTL_RUN_ONCE aRunOnce, PVOID aParameter,
     34                                 PVOID* aContext) {
     35  // Invoke every static initializer in the .freestd section
     36  const GlobalInitializerFn* cur = &FreeStdStart + 1;
     37  while (cur < &FreeStdEnd) {
     38    if (*cur) {
     39      (*cur)();
     40    }
     41 
     42    ++cur;
     43  }
     44 
     45  return TRUE;
     46 }
     47 
     48 /**
     49 * This observer is only used until the mozglue observer connects itself.
     50 * All we do here is accumulate the module loads into a vector.
     51 * As soon as mozglue connects, we call |Forward| on mozglue's LoaderObserver
     52 * to pass our vector on for further processing. This object then becomes
     53 * defunct.
     54 */
     55 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS DefaultLoaderObserver final
     56    : public nt::LoaderObserver {
     57 public:
     58  constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {}
     59 
     60  void OnBeginDllLoad(void** aContext,
     61                      PCUNICODE_STRING aRequestedDllName) final {}
     62  bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
     63                        PHANDLE aOutHandle) final {
     64    return false;
     65  }
     66  void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
     67                    ModuleLoadInfo&& aModuleLoadInfo) final;
     68  void Forward(nt::LoaderObserver* aNext) final;
     69  void OnForward(ModuleLoadInfoVec&& aInfo) final {
     70    MOZ_ASSERT_UNREACHABLE("Not valid in freestanding::DefaultLoaderObserver");
     71  }
     72 
     73 private:
     74  mozilla::nt::SRWLock mLock;
     75  ModuleLoadInfoVec* mModuleLoads;
     76 };
     77 
     78 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final
     79    : public LoaderPrivateAPI {
     80 public:
     81  // LoaderAPI
     82  ModuleLoadInfo ConstructAndNotifyBeginDllLoad(
     83      void** aContext, PCUNICODE_STRING aRequestedDllName) final;
     84  bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
     85                        PHANDLE aOutHandle) final;
     86  void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
     87                        ModuleLoadInfo&& aModuleLoadInfo) final;
     88  nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final;
     89  nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final;
     90  nt::LoaderAPI::HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() final;
     91  nt::SharedSection* GetSharedSection() final;
     92 
     93  // LoaderPrivateAPI
     94  void NotifyBeginDllLoad(void** aContext,
     95                          PCUNICODE_STRING aRequestedDllName) final;
     96  void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext,
     97                          PCUNICODE_STRING aRequestedDllName) final;
     98  void SetObserver(nt::LoaderObserver* aNewObserver) final;
     99  bool IsDefaultObserver() const final;
    100  nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final;
    101 };
    102 
    103 static void Init() {
    104  DebugOnly<NTSTATUS> ntStatus =
    105      ::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr);
    106  MOZ_ASSERT(NT_SUCCESS(ntStatus));
    107 }
    108 
    109 }  // namespace freestanding
    110 }  // namespace mozilla
    111 
    112 static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver;
    113 static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI;
    114 
    115 static mozilla::nt::SRWLock gLoaderObserverLock;
    116 static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver;
    117 static CONDITION_VARIABLE gLoaderObserverNoOngoingLoadsCV =
    118    CONDITION_VARIABLE_INIT;
    119 static mozilla::Atomic<uint32_t> gLoaderObserverOngoingLoadsCount(0);
    120 
    121 namespace mozilla {
    122 namespace freestanding {
    123 
    124 LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI;
    125 
    126 void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
    127                                         ModuleLoadInfo&& aModuleLoadInfo) {
    128  // If the DLL load failed, or if the DLL was loaded by a previous request
    129  // and thus was not mapped by this request, we do not save the ModuleLoadInfo.
    130  if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) {
    131    return;
    132  }
    133 
    134  nt::AutoExclusiveLock lock(mLock);
    135  if (!mModuleLoads) {
    136    mModuleLoads = RtlNew<ModuleLoadInfoVec>();
    137    if (!mModuleLoads) {
    138      return;
    139    }
    140  }
    141 
    142  (void)mModuleLoads->emplaceBack(
    143      std::forward<ModuleLoadInfo>(aModuleLoadInfo));
    144 }
    145 
    146 /**
    147 * Pass mModuleLoads's data off to |aNext| for further processing.
    148 */
    149 void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) {
    150  MOZ_ASSERT(aNext);
    151  if (!aNext) {
    152    return;
    153  }
    154 
    155  ModuleLoadInfoVec* moduleLoads = nullptr;
    156 
    157  {  // Scope for lock
    158    nt::AutoExclusiveLock lock(mLock);
    159    moduleLoads = mModuleLoads;
    160    mModuleLoads = nullptr;
    161  }
    162 
    163  if (!moduleLoads) {
    164    return;
    165  }
    166 
    167  aNext->OnForward(std::move(*moduleLoads));
    168  RtlDelete(moduleLoads);
    169 }
    170 
    171 ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad(
    172    void** aContext, PCUNICODE_STRING aRequestedDllName) {
    173  ModuleLoadInfo loadInfo(aRequestedDllName);
    174 
    175  NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName);
    176 
    177  return loadInfo;
    178 }
    179 
    180 bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
    181                                           PHANDLE aOutHandle) {
    182  // This method should only be called between NotifyBeginDllLoad and
    183  // NotifyEndDllLoad, so it is already protected against gLoaderObserver
    184  // change, through gLoaderObserverOngoingLoadsCount.
    185  MOZ_RELEASE_ASSERT(gLoaderObserverOngoingLoadsCount);
    186 
    187  return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle);
    188 }
    189 
    190 void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext,
    191                                           NTSTATUS aLoadNtStatus,
    192                                           ModuleLoadInfo&& aModuleLoadInfo) {
    193  aModuleLoadInfo.SetEndLoadTimeStamp();
    194 
    195  if (NT_SUCCESS(aLoadNtStatus)) {
    196    aModuleLoadInfo.CaptureBacktrace();
    197  }
    198 
    199  // This method should only be called after a matching call to
    200  // NotifyBeginDllLoad, so it is already protected against gLoaderObserver
    201  // change, through gLoaderObserverOngoingLoadsCount.
    202 
    203  // We need to notify the observer that the DLL load has ended even when
    204  // |aLoadNtStatus| indicates a failure. This is to ensure that any resources
    205  // acquired by the observer during OnBeginDllLoad are cleaned up.
    206  gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus,
    207                                std::move(aModuleLoadInfo));
    208 
    209  auto previousValue = gLoaderObserverOngoingLoadsCount--;
    210  MOZ_RELEASE_ASSERT(previousValue);
    211  if (previousValue == 1) {
    212    ::RtlWakeAllConditionVariable(&gLoaderObserverNoOngoingLoadsCV);
    213  }
    214 }
    215 
    216 nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName(
    217    void* aSectionAddr) {
    218  const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
    219 
    220  nt::MemorySectionNameBuf buf;
    221  NTSTATUS ntStatus =
    222      ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
    223                             &buf, sizeof(buf), nullptr);
    224  if (!NT_SUCCESS(ntStatus)) {
    225    return nt::AllocatedUnicodeString();
    226  }
    227 
    228  return nt::AllocatedUnicodeString(&buf.mSectionFileName);
    229 }
    230 
    231 nt::LoaderAPI::InitDllBlocklistOOPFnPtr
    232 LoaderPrivateAPIImp::GetDllBlocklistInitFn() {
    233  return &InitializeDllBlocklistOOP;
    234 }
    235 
    236 nt::LoaderAPI::HandleLauncherErrorFnPtr
    237 LoaderPrivateAPIImp::GetHandleLauncherErrorFn() {
    238  return &HandleLauncherError;
    239 }
    240 
    241 nt::SharedSection* LoaderPrivateAPIImp::GetSharedSection() {
    242  return &gSharedSection;
    243 }
    244 
    245 nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer(
    246    void* aSectionAddr) {
    247  const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
    248 
    249  nt::MemorySectionNameBuf buf;
    250  NTSTATUS ntStatus =
    251      ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
    252                             &buf, sizeof(buf), nullptr);
    253  if (!NT_SUCCESS(ntStatus)) {
    254    return nt::MemorySectionNameBuf();
    255  }
    256 
    257  return buf;
    258 }
    259 
    260 void LoaderPrivateAPIImp::NotifyBeginDllLoad(
    261    void** aContext, PCUNICODE_STRING aRequestedDllName) {
    262  nt::AutoSharedLock lock(gLoaderObserverLock);
    263  ++gLoaderObserverOngoingLoadsCount;
    264  gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
    265 }
    266 
    267 void LoaderPrivateAPIImp::NotifyBeginDllLoad(
    268    ModuleLoadInfo& aModuleLoadInfo, void** aContext,
    269    PCUNICODE_STRING aRequestedDllName) {
    270  NotifyBeginDllLoad(aContext, aRequestedDllName);
    271  aModuleLoadInfo.SetBeginLoadTimeStamp();
    272 }
    273 
    274 void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) {
    275  nt::LoaderObserver* prevLoaderObserver = nullptr;
    276 
    277  nt::AutoExclusiveLock lock(gLoaderObserverLock);
    278  while (gLoaderObserverOngoingLoadsCount) {
    279    NTSTATUS status = ::RtlSleepConditionVariableSRW(
    280        &gLoaderObserverNoOngoingLoadsCV, &gLoaderObserverLock, nullptr, 0);
    281    MOZ_RELEASE_ASSERT(NT_SUCCESS(status) && status != STATUS_TIMEOUT);
    282  }
    283 
    284  MOZ_ASSERT(aNewObserver);
    285  if (!aNewObserver) {
    286    // This is unlikely, but we always want a valid observer, so use the
    287    // gDefaultObserver if necessary.
    288    gLoaderObserver = &gDefaultObserver;
    289    return;
    290  }
    291 
    292  prevLoaderObserver = gLoaderObserver;
    293  gLoaderObserver = aNewObserver;
    294 
    295  MOZ_ASSERT(prevLoaderObserver);
    296  if (!prevLoaderObserver) {
    297    return;
    298  }
    299 
    300  // Now that we have a new observer, the previous observer must forward its
    301  // data on to the new observer for processing.
    302  prevLoaderObserver->Forward(aNewObserver);
    303 }
    304 
    305 bool LoaderPrivateAPIImp::IsDefaultObserver() const {
    306  nt::AutoSharedLock lock(gLoaderObserverLock);
    307  return gLoaderObserver == &gDefaultObserver;
    308 }
    309 
    310 void EnsureInitialized() { Init(); }
    311 
    312 }  // namespace freestanding
    313 }  // namespace mozilla