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 http://mozilla.org/MPL/2.0/. */
6 
7 #include "LoaderObserver.h"
8 
9 #include "mozilla/AutoProfilerLabel.h"
10 #include "mozilla/BaseProfilerMarkers.h"
11 #include "mozilla/glue/WindowsUnicode.h"
12 #include "mozilla/StackWalk_windows.h"
13 #include "mozilla/TimeStamp.h"
14 
15 namespace {
16 
17 struct LoadContext {
LoadContext__anond0ef0d1c0111::LoadContext18   LoadContext(mozilla::ProfilerLabel&& aLabel,
19               mozilla::UniquePtr<char[]>&& aDynamicStringStorage)
20       : mProfilerLabel(std::move(aLabel)),
21         mDynamicStringStorage(std::move(aDynamicStringStorage)),
22         mStartTime(mozilla::TimeStamp::Now()) {}
23   mozilla::ProfilerLabel mProfilerLabel;
24   mozilla::UniquePtr<char[]> mDynamicStringStorage;
25   mozilla::TimeStamp mStartTime;
26 };
27 
28 }  // anonymous namespace
29 
30 namespace mozilla {
31 
32 extern glue::Win32SRWLock gDllServicesLock;
33 extern glue::detail::DllServicesBase* gDllServices;
34 
35 namespace glue {
36 
OnBeginDllLoad(void ** aContext,PCUNICODE_STRING aRequestedDllName)37 void LoaderObserver::OnBeginDllLoad(void** aContext,
38                                     PCUNICODE_STRING aRequestedDllName) {
39   MOZ_ASSERT(aContext);
40   if (IsProfilerPresent()) {
41     UniquePtr<char[]> utf8RequestedDllName(WideToUTF8(aRequestedDllName));
42     const char* dynamicString = utf8RequestedDllName.get();
43     *aContext = new LoadContext(
44         ProfilerLabelBegin("mozilla::glue::LoaderObserver::OnBeginDllLoad",
45                            dynamicString, &aContext),
46         std::move(utf8RequestedDllName));
47   }
48 
49 #ifdef _M_AMD64
50   // Prevent the stack walker from suspending this thread when LdrLoadDll
51   // holds the RtlLookupFunctionEntry lock.
52   SuppressStackWalking();
53 #endif
54 }
55 
SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,PHANDLE aOutHandle)56 bool LoaderObserver::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
57                                       PHANDLE aOutHandle) {
58   // Currently unsupported
59   return false;
60 }
61 
OnEndDllLoad(void * aContext,NTSTATUS aNtStatus,ModuleLoadInfo && aModuleLoadInfo)62 void LoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
63                                   ModuleLoadInfo&& aModuleLoadInfo) {
64 #ifdef _M_AMD64
65   DesuppressStackWalking();
66 #endif
67 
68   UniquePtr<LoadContext> loadContext(static_cast<LoadContext*>(aContext));
69   if (loadContext && IsValidProfilerLabel(loadContext->mProfilerLabel)) {
70     ProfilerLabelEnd(loadContext->mProfilerLabel);
71     BASE_PROFILER_MARKER_TEXT(
72         "DllLoad", OTHER,
73         MarkerTiming::IntervalUntilNowFrom(loadContext->mStartTime),
74         mozilla::ProfilerString8View::WrapNullTerminatedString(
75             loadContext->mDynamicStringStorage.get()));
76   }
77 
78   // We want to record a denied DLL load regardless of |aNtStatus| because
79   // |aNtStatus| is set to access-denied when DLL load was blocked.
80   if ((!NT_SUCCESS(aNtStatus) && !aModuleLoadInfo.WasDenied()) ||
81       !aModuleLoadInfo.WasMapped()) {
82     return;
83   }
84 
85   {  // Scope for lock
86     AutoSharedLock lock(gDllServicesLock);
87     if (gDllServices) {
88       gDllServices->DispatchDllLoadNotification(std::move(aModuleLoadInfo));
89       return;
90     }
91   }
92 
93   // No dll services, save for later
94   AutoExclusiveLock lock(mLock);
95   if (!mEnabled) {
96     return;
97   }
98 
99   if (!mModuleLoads) {
100     mModuleLoads = new ModuleLoadInfoVec();
101   }
102 
103   Unused << mModuleLoads->emplaceBack(
104       std::forward<ModuleLoadInfo>(aModuleLoadInfo));
105 }
106 
Forward(nt::LoaderObserver * aNext)107 void LoaderObserver::Forward(nt::LoaderObserver* aNext) {
108   MOZ_ASSERT_UNREACHABLE(
109       "This implementation does not forward to any more "
110       "nt::LoaderObserver objects");
111 }
112 
Forward(detail::DllServicesBase * aNext)113 void LoaderObserver::Forward(detail::DllServicesBase* aNext) {
114   MOZ_ASSERT(aNext);
115   if (!aNext) {
116     return;
117   }
118 
119   ModuleLoadInfoVec* moduleLoads = nullptr;
120 
121   {  // Scope for lock
122     AutoExclusiveLock lock(mLock);
123     moduleLoads = mModuleLoads;
124     mModuleLoads = nullptr;
125   }
126 
127   if (!moduleLoads) {
128     return;
129   }
130 
131   aNext->DispatchModuleLoadBacklogNotification(std::move(*moduleLoads));
132   delete moduleLoads;
133 }
134 
Disable()135 void LoaderObserver::Disable() {
136   ModuleLoadInfoVec* moduleLoads = nullptr;
137 
138   {  // Scope for lock
139     AutoExclusiveLock lock(mLock);
140     moduleLoads = mModuleLoads;
141     mModuleLoads = nullptr;
142     mEnabled = false;
143   }
144 
145   delete moduleLoads;
146 }
147 
OnForward(ModuleLoadInfoVec && aInfo)148 void LoaderObserver::OnForward(ModuleLoadInfoVec&& aInfo) {
149   AutoExclusiveLock lock(mLock);
150   if (!mModuleLoads) {
151     mModuleLoads = new ModuleLoadInfoVec();
152   }
153 
154   MOZ_ASSERT(mModuleLoads->empty());
155   if (mModuleLoads->empty()) {
156     *mModuleLoads = std::move(aInfo);
157   } else {
158     // This should not happen, but we can handle it
159     for (auto&& item : aInfo) {
160       Unused << mModuleLoads->append(std::move(item));
161     }
162   }
163 }
164 
165 }  // namespace glue
166 }  // namespace mozilla
167