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