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