diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/build/WindowsDllBlocklist.cpp index ce63a0bc0389..8ac1fe05b2e0 100644 --- a/mozglue/build/WindowsDllBlocklist.cpp +++ b/mozglue/build/WindowsDllBlocklist.cpp @@ -33,6 +33,7 @@ #include "nsWindowsHelpers.h" #include "WindowsDllBlocklist.h" #include "mozilla/AutoProfilerLabel.h" +#include "mozilla/WindowsDllServices.h" using namespace mozilla; @@ -930,3 +931,140 @@ DllBlocklist_CheckStatus() return false; return true; } + +// ============================================================================ +// This section is for DLL Services +// ============================================================================ + + +static SRWLOCK gDllServicesLock = SRWLOCK_INIT; +static mozilla::detail::DllServicesBase* gDllServices; + +class MOZ_RAII AutoSharedLock final +{ +public: + explicit AutoSharedLock(SRWLOCK& aLock) + : mLock(aLock) + { + ::AcquireSRWLockShared(&aLock); + } + + ~AutoSharedLock() + { + ::ReleaseSRWLockShared(&mLock); + } + + AutoSharedLock(const AutoSharedLock&) = delete; + AutoSharedLock(AutoSharedLock&&) = delete; + AutoSharedLock& operator=(const AutoSharedLock&) = delete; + AutoSharedLock& operator=(AutoSharedLock&&) = delete; + +private: + SRWLOCK& mLock; +}; + +class MOZ_RAII AutoExclusiveLock final +{ +public: + explicit AutoExclusiveLock(SRWLOCK& aLock) + : mLock(aLock) + { + ::AcquireSRWLockExclusive(&aLock); + } + + ~AutoExclusiveLock() + { + ::ReleaseSRWLockExclusive(&mLock); + } + + AutoExclusiveLock(const AutoExclusiveLock&) = delete; + AutoExclusiveLock(AutoExclusiveLock&&) = delete; + AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete; + AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete; + +private: + SRWLOCK& mLock; +}; + +// These types are documented on MSDN but not provided in any SDK headers + +enum DllNotificationReason +{ + LDR_DLL_NOTIFICATION_REASON_LOADED = 1, + LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2 +}; + +typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; + +typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { + ULONG Flags; //Reserved. + PCUNICODE_STRING FullDllName; //The full path name of the DLL module. + PCUNICODE_STRING BaseDllName; //The base file name of the DLL module. + PVOID DllBase; //A pointer to the base address for the DLL in memory. + ULONG SizeOfImage; //The size of the DLL image, in bytes. +} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; + +typedef union _LDR_DLL_NOTIFICATION_DATA { + LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; + LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; +} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; + +typedef const LDR_DLL_NOTIFICATION_DATA* PCLDR_DLL_NOTIFICATION_DATA; + +typedef VOID (CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)( + ULONG aReason, + PCLDR_DLL_NOTIFICATION_DATA aNotificationData, + PVOID aContext); + +NTSTATUS NTAPI +LdrRegisterDllNotification(ULONG aFlags, + PLDR_DLL_NOTIFICATION_FUNCTION aCallback, + PVOID aContext, PVOID* aCookie); + +static PVOID gNotificationCookie; + +static VOID CALLBACK +DllLoadNotification(ULONG aReason, PCLDR_DLL_NOTIFICATION_DATA aNotificationData, + PVOID aContext) +{ + if (aReason != LDR_DLL_NOTIFICATION_REASON_LOADED) { + // We don't care about unloads + return; + } + + AutoSharedLock lock(gDllServicesLock); + if (!gDllServices) { + return; + } + + PCUNICODE_STRING fullDllName = aNotificationData->Loaded.FullDllName; + gDllServices->DispatchDllLoadNotification(fullDllName); +} + +MFBT_API void +DllBlocklist_SetDllServices(mozilla::detail::DllServicesBase* aSvc) +{ + AutoExclusiveLock lock(gDllServicesLock); + + if (aSvc && !gNotificationCookie) { + auto pLdrRegisterDllNotification = + reinterpret_cast( + ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), + "LdrRegisterDllNotification")); + + MOZ_DIAGNOSTIC_ASSERT(pLdrRegisterDllNotification); + + NTSTATUS ntStatus = pLdrRegisterDllNotification(0, &DllLoadNotification, + nullptr, &gNotificationCookie); + MOZ_DIAGNOSTIC_ASSERT(NT_SUCCESS(ntStatus)); + } + + gDllServices = aSvc; +} + diff --git a/mozglue/build/WindowsDllBlocklist.h b/mozglue/build/WindowsDllBlocklist.h index 2a27e592dddd..b69335baa952 100644 --- a/mozglue/build/WindowsDllBlocklist.h +++ b/mozglue/build/WindowsDllBlocklist.h @@ -24,5 +24,14 @@ MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags = eDllBlocklistInitFla MFBT_API void DllBlocklist_WriteNotes(HANDLE file); MFBT_API bool DllBlocklist_CheckStatus(); +// Forward declaration +namespace mozilla { +namespace detail { +class DllServicesBase; +} // namespace detail +} // namespace mozilla + +MFBT_API void DllBlocklist_SetDllServices(mozilla::detail::DllServicesBase* aSvc); + #endif // defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) #endif // mozilla_windowsdllblocklist_h diff --git a/mozglue/build/WindowsDllServices.h b/mozglue/build/WindowsDllServices.h new file mode 100644 index 000000000000..1632ca1f7aa1 --- /dev/null +++ b/mozglue/build/WindowsDllServices.h @@ -0,0 +1,91 @@ +/* -*- 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_WindowsDllServices_h +#define mozilla_WindowsDllServices_h + +#include "mozilla/WindowsDllBlocklist.h" + +#if defined(MOZILLA_INTERNAL_API) + +#include "mozilla/SystemGroup.h" +#include "nsISupportsImpl.h" +#include "nsString.h" +#include "nsThreadUtils.h" + +#endif // defined(MOZILLA_INTERNAL_API) + +// For PCUNICODE_STRING +#include + +namespace mozilla { +namespace detail { + +class DllServicesBase +{ +public: + /** + * WARNING: This method is called from within an unsafe context that holds + * multiple locks inside the Windows loader. The only thing that + * this function should be used for is dispatching the event to our + * event loop so that it may be handled in a safe context. + */ + virtual void DispatchDllLoadNotification(PCUNICODE_STRING aDllName) = 0; + + void Disable() + { + DllBlocklist_SetDllServices(nullptr); + } + + DllServicesBase(const DllServicesBase&) = delete; + DllServicesBase(DllServicesBase&&) = delete; + DllServicesBase& operator=(const DllServicesBase&) = delete; + DllServicesBase& operator=(DllServicesBase&&) = delete; + +protected: + DllServicesBase() = default; + virtual ~DllServicesBase() = default; + + void Enable() + { + DllBlocklist_SetDllServices(this); + } +}; + +} // namespace detail + +#if defined(MOZILLA_INTERNAL_API) + +class DllServices : public detail::DllServicesBase +{ +public: + virtual void DispatchDllLoadNotification(PCUNICODE_STRING aDllName) override final + { + nsDependentString strDllName(aDllName->Buffer, + aDllName->Length / sizeof(wchar_t)); + + nsCOMPtr runnable( + NewRunnableMethod("DllServices::NotifyDllLoad", + this, &DllServices::NotifyDllLoad, + NS_IsMainThread(), strDllName)); + + SystemGroup::Dispatch(TaskCategory::Other, runnable.forget()); + } + + NS_INLINE_DECL_THREADSAFE_VIRTUAL_REFCOUNTING(DllServices) + +protected: + DllServices() = default; + ~DllServices() = default; + + virtual void NotifyDllLoad(const bool aIsMainThread, const nsString& aDllName) = 0; +}; + +#endif // defined(MOZILLA_INTERNAL_API) + +} // namespace mozilla + +#endif // mozilla_WindowsDllServices_h diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build index 9970259601b9..e5742816c179 100644 --- a/mozglue/build/moz.build +++ b/mozglue/build/moz.build @@ -57,6 +57,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT']: 'mips.h', 'SSE.h', 'WindowsDllBlocklist.h', + 'WindowsDllServices.h', ] if CONFIG['CPU_ARCH'].startswith('x86'): diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index c52cde360024..17a88fc4bf17 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -159,6 +159,7 @@ #ifdef XP_WIN #include #include +#include "mozilla/WindowsDllServices.h" #include "nsThreadUtils.h" #include #include @@ -1654,6 +1655,40 @@ ScopedXPCOMStartup::CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport; +#if defined(XP_WIN) + +class DllNotifications : public mozilla::DllServices +{ +public: + DllNotifications() + { + Enable(); + } + +private: + ~DllNotifications() = default; + + void NotifyDllLoad(const bool aIsMainThread, const nsString& aDllName) override; +}; + +void +DllNotifications::NotifyDllLoad(const bool aIsMainThread, + const nsString& aDllName) +{ + const char* topic; + + if (aIsMainThread) { + topic = "dll-loaded-main-thread"; + } else { + topic = "dll-loaded-non-main-thread"; + } + + nsCOMPtr obsServ(mozilla::services::GetObserverService()); + obsServ->NotifyObservers(nullptr, topic, aDllName.get()); +} + +#endif // defined(XP_WIN) + static void DumpArbitraryHelp() { nsresult rv; @@ -4302,6 +4337,13 @@ XREMain::XRE_mainRun() nsresult rv = NS_OK; NS_ASSERTION(mScopedXPCOM, "Scoped xpcom not initialized."); +#if defined(XP_WIN) + RefPtr dllNotifications(new DllNotifications()); + auto dllNotificationsDisable = MakeScopeExit([&dllNotifications]() { + dllNotifications->Disable(); + }); +#endif // defined(XP_WIN) + #ifdef NS_FUNCTION_TIMER // initialize some common services, so we don't pay the cost for these at odd times later on; // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs