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 https://mozilla.org/MPL/2.0/. */
6 
7 #include "LoaderPrivateAPI.h"
8 
9 #include "mozilla/Assertions.h"
10 #include "mozilla/Types.h"
11 #include "mozilla/Unused.h"
12 #include "../DllBlocklistInit.h"
13 
14 using GlobalInitializerFn = void(__cdecl*)(void);
15 
16 // Allocation of static initialization section for the freestanding library
17 #pragma section(".freestd$a", read)
18 __declspec(allocate(".freestd$a")) static const GlobalInitializerFn
19     FreeStdStart = reinterpret_cast<GlobalInitializerFn>(0);
20 
21 #pragma section(".freestd$z", read)
22 __declspec(allocate(".freestd$z")) static const GlobalInitializerFn FreeStdEnd =
23     reinterpret_cast<GlobalInitializerFn>(0);
24 
25 namespace mozilla {
26 namespace freestanding {
27 
28 static RTL_RUN_ONCE gRunOnce = RTL_RUN_ONCE_INIT;
29 
30 // The contract for this callback is identical to the InitOnceCallback from
31 // Win32 land; we're just using ntdll-layer types instead.
DoOneTimeInit(PRTL_RUN_ONCE aRunOnce,PVOID aParameter,PVOID * aContext)32 static ULONG NTAPI DoOneTimeInit(PRTL_RUN_ONCE aRunOnce, PVOID aParameter,
33                                  PVOID* aContext) {
34   // Invoke every static initializer in the .freestd section
35   const GlobalInitializerFn* cur = &FreeStdStart + 1;
36   while (cur < &FreeStdEnd) {
37     if (*cur) {
38       (*cur)();
39     }
40 
41     ++cur;
42   }
43 
44   return TRUE;
45 }
46 
47 /**
48  * This observer is only used until the mozglue observer connects itself.
49  * All we do here is accumulate the module loads into a vector.
50  * As soon as mozglue connects, we call |Forward| on mozglue's LoaderObserver
51  * to pass our vector on for further processing. This object then becomes
52  * defunct.
53  */
54 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS DefaultLoaderObserver final
55     : public nt::LoaderObserver {
56  public:
DefaultLoaderObserver()57   constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {}
58 
OnBeginDllLoad(void ** aContext,PCUNICODE_STRING aRequestedDllName)59   void OnBeginDllLoad(void** aContext,
60                       PCUNICODE_STRING aRequestedDllName) final {}
SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,PHANDLE aOutHandle)61   bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
62                         PHANDLE aOutHandle) final {
63     return false;
64   }
65   void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
66                     ModuleLoadInfo&& aModuleLoadInfo) final;
67   void Forward(nt::LoaderObserver* aNext) final;
OnForward(ModuleLoadInfoVec && aInfo)68   void OnForward(ModuleLoadInfoVec&& aInfo) final {
69     MOZ_ASSERT_UNREACHABLE("Not valid in freestanding::DefaultLoaderObserver");
70   }
71 
72  private:
73   mozilla::nt::SRWLock mLock;
74   ModuleLoadInfoVec* mModuleLoads;
75 };
76 
77 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final
78     : public LoaderPrivateAPI {
79  public:
80   // LoaderAPI
81   ModuleLoadInfo ConstructAndNotifyBeginDllLoad(
82       void** aContext, PCUNICODE_STRING aRequestedDllName) final;
83   bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
84                         PHANDLE aOutHandle) final;
85   void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
86                         ModuleLoadInfo&& aModuleLoadInfo) final;
87   nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final;
88   nt::LoaderAPI::InitDllBlocklistOOPFnPtr GetDllBlocklistInitFn() final;
89 
90   // LoaderPrivateAPI
91   void NotifyBeginDllLoad(void** aContext,
92                           PCUNICODE_STRING aRequestedDllName) final;
93   void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext,
94                           PCUNICODE_STRING aRequestedDllName) final;
95   void SetObserver(nt::LoaderObserver* aNewObserver) final;
96   bool IsDefaultObserver() const final;
97   nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final;
98 };
99 
Init()100 static void Init() {
101   DebugOnly<NTSTATUS> ntStatus =
102       ::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr);
103   MOZ_ASSERT(NT_SUCCESS(ntStatus));
104 }
105 
106 }  // namespace freestanding
107 }  // namespace mozilla
108 
109 static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver;
110 static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI;
111 
112 static mozilla::nt::SRWLock gLoaderObserverLock;
113 static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver;
114 
115 namespace mozilla {
116 namespace freestanding {
117 
118 LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI;
119 
OnEndDllLoad(void * aContext,NTSTATUS aNtStatus,ModuleLoadInfo && aModuleLoadInfo)120 void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus,
121                                          ModuleLoadInfo&& aModuleLoadInfo) {
122   // If the DLL load failed, or if the DLL was loaded by a previous request
123   // and thus was not mapped by this request, we do not save the ModuleLoadInfo.
124   if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) {
125     return;
126   }
127 
128   nt::AutoExclusiveLock lock(mLock);
129   if (!mModuleLoads) {
130     mModuleLoads = RtlNew<ModuleLoadInfoVec>();
131     if (!mModuleLoads) {
132       return;
133     }
134   }
135 
136   Unused << mModuleLoads->emplaceBack(
137       std::forward<ModuleLoadInfo>(aModuleLoadInfo));
138 }
139 
140 /**
141  * Pass mModuleLoads's data off to |aNext| for further processing.
142  */
Forward(nt::LoaderObserver * aNext)143 void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) {
144   MOZ_ASSERT(aNext);
145   if (!aNext) {
146     return;
147   }
148 
149   ModuleLoadInfoVec* moduleLoads = nullptr;
150 
151   {  // Scope for lock
152     nt::AutoExclusiveLock lock(mLock);
153     moduleLoads = mModuleLoads;
154     mModuleLoads = nullptr;
155   }
156 
157   if (!moduleLoads) {
158     return;
159   }
160 
161   aNext->OnForward(std::move(*moduleLoads));
162   RtlDelete(moduleLoads);
163 }
164 
ConstructAndNotifyBeginDllLoad(void ** aContext,PCUNICODE_STRING aRequestedDllName)165 ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad(
166     void** aContext, PCUNICODE_STRING aRequestedDllName) {
167   ModuleLoadInfo loadInfo(aRequestedDllName);
168 
169   NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName);
170 
171   return loadInfo;
172 }
173 
SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,PHANDLE aOutHandle)174 bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName,
175                                            PHANDLE aOutHandle) {
176   nt::AutoSharedLock lock(gLoaderObserverLock);
177   return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle);
178 }
179 
NotifyEndDllLoad(void * aContext,NTSTATUS aLoadNtStatus,ModuleLoadInfo && aModuleLoadInfo)180 void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext,
181                                            NTSTATUS aLoadNtStatus,
182                                            ModuleLoadInfo&& aModuleLoadInfo) {
183   aModuleLoadInfo.SetEndLoadTimeStamp();
184 
185   if (NT_SUCCESS(aLoadNtStatus)) {
186     aModuleLoadInfo.CaptureBacktrace();
187   }
188 
189   nt::AutoSharedLock lock(gLoaderObserverLock);
190 
191   // We need to notify the observer that the DLL load has ended even when
192   // |aLoadNtStatus| indicates a failure. This is to ensure that any resources
193   // acquired by the observer during OnBeginDllLoad are cleaned up.
194   gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus,
195                                 std::move(aModuleLoadInfo));
196 }
197 
GetSectionName(void * aSectionAddr)198 nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName(
199     void* aSectionAddr) {
200   const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
201 
202   nt::MemorySectionNameBuf buf;
203   NTSTATUS ntStatus =
204       ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
205                              &buf, sizeof(buf), nullptr);
206   if (!NT_SUCCESS(ntStatus)) {
207     return nt::AllocatedUnicodeString();
208   }
209 
210   return nt::AllocatedUnicodeString(&buf.mSectionFileName);
211 }
212 
213 nt::LoaderAPI::InitDllBlocklistOOPFnPtr
GetDllBlocklistInitFn()214 LoaderPrivateAPIImp::GetDllBlocklistInitFn() {
215   return &InitializeDllBlocklistOOP;
216 }
217 
GetSectionNameBuffer(void * aSectionAddr)218 nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer(
219     void* aSectionAddr) {
220   const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
221 
222   nt::MemorySectionNameBuf buf;
223   NTSTATUS ntStatus =
224       ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
225                              &buf, sizeof(buf), nullptr);
226   if (!NT_SUCCESS(ntStatus)) {
227     return nt::MemorySectionNameBuf();
228   }
229 
230   return buf;
231 }
232 
NotifyBeginDllLoad(void ** aContext,PCUNICODE_STRING aRequestedDllName)233 void LoaderPrivateAPIImp::NotifyBeginDllLoad(
234     void** aContext, PCUNICODE_STRING aRequestedDllName) {
235   nt::AutoSharedLock lock(gLoaderObserverLock);
236   gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
237 }
238 
NotifyBeginDllLoad(ModuleLoadInfo & aModuleLoadInfo,void ** aContext,PCUNICODE_STRING aRequestedDllName)239 void LoaderPrivateAPIImp::NotifyBeginDllLoad(
240     ModuleLoadInfo& aModuleLoadInfo, void** aContext,
241     PCUNICODE_STRING aRequestedDllName) {
242   NotifyBeginDllLoad(aContext, aRequestedDllName);
243   aModuleLoadInfo.SetBeginLoadTimeStamp();
244 }
245 
SetObserver(nt::LoaderObserver * aNewObserver)246 void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) {
247   nt::LoaderObserver* prevLoaderObserver = nullptr;
248 
249   nt::AutoExclusiveLock lock(gLoaderObserverLock);
250 
251   MOZ_ASSERT(aNewObserver);
252   if (!aNewObserver) {
253     // This is unlikely, but we always want a valid observer, so use the
254     // gDefaultObserver if necessary.
255     gLoaderObserver = &gDefaultObserver;
256     return;
257   }
258 
259   prevLoaderObserver = gLoaderObserver;
260   gLoaderObserver = aNewObserver;
261 
262   MOZ_ASSERT(prevLoaderObserver);
263   if (!prevLoaderObserver) {
264     return;
265   }
266 
267   // Now that we have a new observer, the previous observer must forward its
268   // data on to the new observer for processing.
269   prevLoaderObserver->Forward(aNewObserver);
270 }
271 
IsDefaultObserver() const272 bool LoaderPrivateAPIImp::IsDefaultObserver() const {
273   nt::AutoSharedLock lock(gLoaderObserverLock);
274   return gLoaderObserver == &gDefaultObserver;
275 }
276 
EnsureInitialized()277 void EnsureInitialized() { Init(); }
278 
279 }  // namespace freestanding
280 }  // namespace mozilla
281