Bug 1422394: Add a mechanism to report dll loads to an observer; r=jimm
☠☠ backed out by ef5dabc27585 ☠ ☠
authorAaron Klotz <aklotz@mozilla.com>
Mon, 04 Dec 2017 18:08:17 -0700
changeset 449468 96940dc4db4491f6feec04cb19a812a81b94b9e2
parent 449467 e39d8a15596a0c97ea1ce19194ce43af59cfbb60
child 449469 096055749c605a1f51c0b66d83be81292e93a586
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1422394
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1422394: Add a mechanism to report dll loads to an observer; r=jimm
mozglue/build/WindowsDllBlocklist.cpp
mozglue/build/WindowsDllBlocklist.h
mozglue/build/WindowsDllServices.h
mozglue/build/moz.build
toolkit/xre/nsAppRunner.cpp
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -28,16 +28,17 @@
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/StackWalk_windows.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsHelpers.h"
 #include "WindowsDllBlocklist.h"
 #include "mozilla/AutoProfilerLabel.h"
+#include "mozilla/WindowsDllServices.h"
 
 using namespace mozilla;
 
 #define ALL_VERSIONS   ((unsigned long long)-1LL)
 
 // DLLs sometimes ship without a version number, particularly early
 // releases. Blocking "version <= 0" has the effect of blocking unversioned
 // DLLs (since the call to get version info fails), but not blocking
@@ -925,8 +926,145 @@ DllBlocklist_WriteNotes(HANDLE file)
 
 MFBT_API bool
 DllBlocklist_CheckStatus()
 {
   if (sBlocklistInitFailed || sUser32BeforeBlocklist)
     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<decltype(&::LdrRegisterDllNotification)>(
+        ::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;
+}
+
--- a/mozglue/build/WindowsDllBlocklist.h
+++ b/mozglue/build/WindowsDllBlocklist.h
@@ -19,10 +19,19 @@ enum DllBlocklistInitFlags
   eDllBlocklistInitFlagDefault = 0,
   eDllBlocklistInitFlagIsChildProcess = 1
 };
 
 MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags = eDllBlocklistInitFlagDefault);
 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
new file mode 100644
--- /dev/null
+++ b/mozglue/build/WindowsDllServices.h
@@ -0,0 +1,87 @@
+/* -*- 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 "nsString.h"
+#include "nsThreadUtils.h"
+
+#endif // defined(MOZILLA_INTERNAL_API)
+
+// For PCUNICODE_STRING
+#include <winternl.h>
+
+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;
+
+  DllServicesBase(const DllServicesBase&) = delete;
+  DllServicesBase(DllServicesBase&&) = delete;
+  DllServicesBase& operator=(const DllServicesBase&) = delete;
+  DllServicesBase& operator=(DllServicesBase&&) = delete;
+
+protected:
+  DllServicesBase() = default;
+
+  virtual ~DllServicesBase()
+  {
+    DllBlocklist_SetDllServices(nullptr);
+  }
+
+  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<nsIRunnable> runnable(
+      NewNonOwningRunnableMethod<bool, nsString>("DllServices::NotifyDllLoad",
+                                                 this,
+                                                 &DllServices::NotifyDllLoad,
+                                                 NS_IsMainThread(), strDllName));
+
+    SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
+  }
+
+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
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -52,16 +52,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT']:
             'version',
         ]
 
     EXPORTS.mozilla += [
         'arm.h',
         'mips.h',
         'SSE.h',
         'WindowsDllBlocklist.h',
+        'WindowsDllServices.h',
     ]
 
     if CONFIG['CPU_ARCH'].startswith('x86'):
         SOURCES += [
             'SSE.cpp',
         ]
 
     if CONFIG['CPU_ARCH'] == 'arm':
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -154,16 +154,17 @@
 #include <sys/stat.h>
 #include <unistd.h>
 #include <pwd.h>
 #endif
 
 #ifdef XP_WIN
 #include <process.h>
 #include <shlobj.h>
+#include "mozilla/WindowsDllServices.h"
 #include "nsThreadUtils.h"
 #include <comdef.h>
 #include <wbemidl.h>
 #include "WinUtils.h"
 #endif
 
 #ifdef XP_MACOSX
 #include "nsILocalFileMac.h"
@@ -1649,16 +1650,50 @@ ScopedXPCOMStartup::CreateAppSupport(nsI
   if (!gNativeAppSupport)
     return NS_ERROR_NOT_INITIALIZED;
 
   return gNativeAppSupport->QueryInterface(aIID, aResult);
 }
 
 nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport;
 
+#if defined(XP_WIN)
+
+class DllNotifications : public mozilla::DllServices
+{
+public:
+  DllNotifications()
+  {
+    Enable();
+  }
+
+  ~DllNotifications() = default;
+
+private:
+  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<nsIObserverService> obsServ(mozilla::services::GetObserverService());
+  obsServ->NotifyObservers(nullptr, topic, aDllName.get());
+}
+
+#endif // defined(XP_WIN)
+
 static void DumpArbitraryHelp()
 {
   nsresult rv;
 
   ScopedLogging log;
 
   {
     ScopedXPCOMStartup xpcom;
@@ -4297,16 +4332,20 @@ void AddSandboxAnnotations()
  * the calling of appStartup->Run().
  */
 nsresult
 XREMain::XRE_mainRun()
 {
   nsresult rv = NS_OK;
   NS_ASSERTION(mScopedXPCOM, "Scoped xpcom not initialized.");
 
+#if defined(XP_WIN)
+  DllNotifications dllNotifications;
+#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
   {
     nsCOMPtr<nsISupports> comp;
 
     comp = do_GetService("@mozilla.org/preferences-service;1");