summaryrefslogtreecommitdiffstats
path: root/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
parentInitial commit. (diff)
downloadfirefox-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.cpp314
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