diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp')
-rw-r--r-- | browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp new file mode 100644 index 0000000000..fb65a2d405 --- /dev/null +++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp @@ -0,0 +1,314 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "LoaderPrivateAPI.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Types.h" +#include "mozilla/Unused.h" +#include "../DllBlocklistInit.h" +#include "../ErrorHandler.h" +#include "SharedSection.h" + +using GlobalInitializerFn = void(__cdecl*)(void); + +// Allocation of static initialization section for the freestanding library +#pragma section(".freestd$a", read) +__declspec(allocate(".freestd$a")) static const GlobalInitializerFn + FreeStdStart = reinterpret_cast<GlobalInitializerFn>(0); + +#pragma section(".freestd$z", read) +__declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd = + reinterpret_cast<GlobalInitializerFn>(0); + +namespace mozilla { +namespace freestanding { + +static RTL_RUN_ONCE gRunOnce = RTL_RUN_ONCE_INIT; + +// The contract for this callback is identical to the InitOnceCallback from +// Win32 land; we're just using ntdll-layer types instead. +static ULONG NTAPI DoOneTimeInit(PRTL_RUN_ONCE aRunOnce, PVOID aParameter, + PVOID* aContext) { + // Invoke every static initializer in the .freestd section + const GlobalInitializerFn* cur = &FreeStdStart + 1; + while (cur < &FreeStdEnd) { + if (*cur) { + (*cur)(); + } + + ++cur; + } + + return TRUE; +} + +/** + * This observer is only used until the mozglue observer connects itself. + * All we do here is accumulate the module loads into a vector. + * As soon as mozglue connects, we call |Forward| on mozglue's LoaderObserver + * to pass our vector on for further processing. This object then becomes + * defunct. + */ +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS DefaultLoaderObserver final + : public nt::LoaderObserver { + public: + constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {} + + void OnBeginDllLoad(void** aContext, + PCUNICODE_STRING aRequestedDllName) final {} + bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) final { + return false; + } + void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) final; + void Forward(nt::LoaderObserver* aNext) final; + void OnForward(ModuleLoadInfoVec&& aInfo) final { + MOZ_ASSERT_UNREACHABLE("Not valid in freestanding::DefaultLoaderObserver"); + } + + private: + mozilla::nt::SRWLock mLock; + ModuleLoadInfoVec* mModuleLoads; +}; + +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final + : public LoaderPrivateAPI { + public: + // LoaderAPI + ModuleLoadInfo ConstructAndNotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) final; + bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) final; + void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) final; + nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final; + nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final; + nt::LoaderAPI::HandleLauncherErrorFnPtr GetHandleLauncherErrorFn() final; + nt::SharedSection* GetSharedSection() final; + + // LoaderPrivateAPI + void NotifyBeginDllLoad(void** aContext, + PCUNICODE_STRING aRequestedDllName) final; + void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext, + PCUNICODE_STRING aRequestedDllName) final; + void SetObserver(nt::LoaderObserver* aNewObserver) final; + bool IsDefaultObserver() const final; + nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final; +}; + +static void Init() { + DebugOnly<NTSTATUS> ntStatus = + ::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr); + MOZ_ASSERT(NT_SUCCESS(ntStatus)); +} + +} // namespace freestanding +} // namespace mozilla + +static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver; +static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI; + +static mozilla::nt::SRWLock gLoaderObserverLock; +static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver; +static CONDITION_VARIABLE gLoaderObserverNoOngoingLoadsCV = + CONDITION_VARIABLE_INIT; +static mozilla::Atomic<uint32_t> gLoaderObserverOngoingLoadsCount(0); + +namespace mozilla { +namespace freestanding { + +LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI; + +void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) { + // If the DLL load failed, or if the DLL was loaded by a previous request + // and thus was not mapped by this request, we do not save the ModuleLoadInfo. + if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) { + return; + } + + nt::AutoExclusiveLock lock(mLock); + if (!mModuleLoads) { + mModuleLoads = RtlNew<ModuleLoadInfoVec>(); + if (!mModuleLoads) { + return; + } + } + + Unused << mModuleLoads->emplaceBack( + std::forward<ModuleLoadInfo>(aModuleLoadInfo)); +} + +/** + * Pass mModuleLoads's data off to |aNext| for further processing. + */ +void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) { + MOZ_ASSERT(aNext); + if (!aNext) { + return; + } + + ModuleLoadInfoVec* moduleLoads = nullptr; + + { // Scope for lock + nt::AutoExclusiveLock lock(mLock); + moduleLoads = mModuleLoads; + mModuleLoads = nullptr; + } + + if (!moduleLoads) { + return; + } + + aNext->OnForward(std::move(*moduleLoads)); + RtlDelete(moduleLoads); +} + +ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) { + ModuleLoadInfo loadInfo(aRequestedDllName); + + NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName); + + return loadInfo; +} + +bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) { + // This method should only be called between NotifyBeginDllLoad and + // NotifyEndDllLoad, so it is already protected against gLoaderObserver + // change, through gLoaderObserverOngoingLoadsCount. + MOZ_RELEASE_ASSERT(gLoaderObserverOngoingLoadsCount); + + return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle); +} + +void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext, + NTSTATUS aLoadNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) { + aModuleLoadInfo.SetEndLoadTimeStamp(); + + if (NT_SUCCESS(aLoadNtStatus)) { + aModuleLoadInfo.CaptureBacktrace(); + } + + // This method should only be called after a matching call to + // NotifyBeginDllLoad, so it is already protected against gLoaderObserver + // change, through gLoaderObserverOngoingLoadsCount. + + // We need to notify the observer that the DLL load has ended even when + // |aLoadNtStatus| indicates a failure. This is to ensure that any resources + // acquired by the observer during OnBeginDllLoad are cleaned up. + gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus, + std::move(aModuleLoadInfo)); + + auto previousValue = gLoaderObserverOngoingLoadsCount--; + MOZ_RELEASE_ASSERT(previousValue); + if (previousValue == 1) { + ::RtlWakeAllConditionVariable(&gLoaderObserverNoOngoingLoadsCV); + } +} + +nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName( + void* aSectionAddr) { + const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); + + nt::MemorySectionNameBuf buf; + NTSTATUS ntStatus = + ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName, + &buf, sizeof(buf), nullptr); + if (!NT_SUCCESS(ntStatus)) { + return nt::AllocatedUnicodeString(); + } + + return nt::AllocatedUnicodeString(&buf.mSectionFileName); +} + +nt::LoaderAPI::InitDllBlocklistOOPFnPtr +LoaderPrivateAPIImp::GetDllBlocklistInitFn() { + return &InitializeDllBlocklistOOP; +} + +nt::LoaderAPI::HandleLauncherErrorFnPtr +LoaderPrivateAPIImp::GetHandleLauncherErrorFn() { + return &HandleLauncherError; +} + +nt::SharedSection* LoaderPrivateAPIImp::GetSharedSection() { + return &gSharedSection; +} + +nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer( + void* aSectionAddr) { + const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); + + nt::MemorySectionNameBuf buf; + NTSTATUS ntStatus = + ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName, + &buf, sizeof(buf), nullptr); + if (!NT_SUCCESS(ntStatus)) { + return nt::MemorySectionNameBuf(); + } + + return buf; +} + +void LoaderPrivateAPIImp::NotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) { + nt::AutoSharedLock lock(gLoaderObserverLock); + ++gLoaderObserverOngoingLoadsCount; + gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName); +} + +void LoaderPrivateAPIImp::NotifyBeginDllLoad( + ModuleLoadInfo& aModuleLoadInfo, void** aContext, + PCUNICODE_STRING aRequestedDllName) { + NotifyBeginDllLoad(aContext, aRequestedDllName); + aModuleLoadInfo.SetBeginLoadTimeStamp(); +} + +void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) { + nt::LoaderObserver* prevLoaderObserver = nullptr; + + nt::AutoExclusiveLock lock(gLoaderObserverLock); + while (gLoaderObserverOngoingLoadsCount) { + NTSTATUS status = ::RtlSleepConditionVariableSRW( + &gLoaderObserverNoOngoingLoadsCV, &gLoaderObserverLock, nullptr, 0); + MOZ_RELEASE_ASSERT(NT_SUCCESS(status) && status != STATUS_TIMEOUT); + } + + MOZ_ASSERT(aNewObserver); + if (!aNewObserver) { + // This is unlikely, but we always want a valid observer, so use the + // gDefaultObserver if necessary. + gLoaderObserver = &gDefaultObserver; + return; + } + + prevLoaderObserver = gLoaderObserver; + gLoaderObserver = aNewObserver; + + MOZ_ASSERT(prevLoaderObserver); + if (!prevLoaderObserver) { + return; + } + + // Now that we have a new observer, the previous observer must forward its + // data on to the new observer for processing. + prevLoaderObserver->Forward(aNewObserver); +} + +bool LoaderPrivateAPIImp::IsDefaultObserver() const { + nt::AutoSharedLock lock(gLoaderObserverLock); + return gLoaderObserver == &gDefaultObserver; +} + +void EnsureInitialized() { Init(); } + +} // namespace freestanding +} // namespace mozilla |