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