LoaderPrivateAPI.cpp (10472B)
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 "../DllBlocklistInit.h" 11 #include "../ErrorHandler.h" 12 #include "SharedSection.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( 19 ".freestd$a")) static const GlobalInitializerFn FreeStdStart = 20 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. 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: 58 constexpr DefaultLoaderObserver() : mModuleLoads(nullptr) {} 59 60 void OnBeginDllLoad(void** aContext, 61 PCUNICODE_STRING aRequestedDllName) final {} 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; 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 nt::SharedSection* GetSharedSection() final; 92 93 // LoaderPrivateAPI 94 void NotifyBeginDllLoad(void** aContext, 95 PCUNICODE_STRING aRequestedDllName) final; 96 void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext, 97 PCUNICODE_STRING aRequestedDllName) final; 98 void SetObserver(nt::LoaderObserver* aNewObserver) final; 99 bool IsDefaultObserver() const final; 100 nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final; 101 }; 102 103 static void Init() { 104 DebugOnly<NTSTATUS> ntStatus = 105 ::RtlRunOnceExecuteOnce(&gRunOnce, &DoOneTimeInit, nullptr, nullptr); 106 MOZ_ASSERT(NT_SUCCESS(ntStatus)); 107 } 108 109 } // namespace freestanding 110 } // namespace mozilla 111 112 static mozilla::freestanding::DefaultLoaderObserver gDefaultObserver; 113 static mozilla::freestanding::LoaderPrivateAPIImp gPrivateAPI; 114 115 static mozilla::nt::SRWLock gLoaderObserverLock; 116 static mozilla::nt::LoaderObserver* gLoaderObserver = &gDefaultObserver; 117 static CONDITION_VARIABLE gLoaderObserverNoOngoingLoadsCV = 118 CONDITION_VARIABLE_INIT; 119 static mozilla::Atomic<uint32_t> gLoaderObserverOngoingLoadsCount(0); 120 121 namespace mozilla { 122 namespace freestanding { 123 124 LoaderPrivateAPI& gLoaderPrivateAPI = gPrivateAPI; 125 126 void DefaultLoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, 127 ModuleLoadInfo&& aModuleLoadInfo) { 128 // If the DLL load failed, or if the DLL was loaded by a previous request 129 // and thus was not mapped by this request, we do not save the ModuleLoadInfo. 130 if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) { 131 return; 132 } 133 134 nt::AutoExclusiveLock lock(mLock); 135 if (!mModuleLoads) { 136 mModuleLoads = RtlNew<ModuleLoadInfoVec>(); 137 if (!mModuleLoads) { 138 return; 139 } 140 } 141 142 (void)mModuleLoads->emplaceBack( 143 std::forward<ModuleLoadInfo>(aModuleLoadInfo)); 144 } 145 146 /** 147 * Pass mModuleLoads's data off to |aNext| for further processing. 148 */ 149 void DefaultLoaderObserver::Forward(nt::LoaderObserver* aNext) { 150 MOZ_ASSERT(aNext); 151 if (!aNext) { 152 return; 153 } 154 155 ModuleLoadInfoVec* moduleLoads = nullptr; 156 157 { // Scope for lock 158 nt::AutoExclusiveLock lock(mLock); 159 moduleLoads = mModuleLoads; 160 mModuleLoads = nullptr; 161 } 162 163 if (!moduleLoads) { 164 return; 165 } 166 167 aNext->OnForward(std::move(*moduleLoads)); 168 RtlDelete(moduleLoads); 169 } 170 171 ModuleLoadInfo LoaderPrivateAPIImp::ConstructAndNotifyBeginDllLoad( 172 void** aContext, PCUNICODE_STRING aRequestedDllName) { 173 ModuleLoadInfo loadInfo(aRequestedDllName); 174 175 NotifyBeginDllLoad(loadInfo, aContext, aRequestedDllName); 176 177 return loadInfo; 178 } 179 180 bool LoaderPrivateAPIImp::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, 181 PHANDLE aOutHandle) { 182 // This method should only be called between NotifyBeginDllLoad and 183 // NotifyEndDllLoad, so it is already protected against gLoaderObserver 184 // change, through gLoaderObserverOngoingLoadsCount. 185 MOZ_RELEASE_ASSERT(gLoaderObserverOngoingLoadsCount); 186 187 return gLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle); 188 } 189 190 void LoaderPrivateAPIImp::NotifyEndDllLoad(void* aContext, 191 NTSTATUS aLoadNtStatus, 192 ModuleLoadInfo&& aModuleLoadInfo) { 193 aModuleLoadInfo.SetEndLoadTimeStamp(); 194 195 if (NT_SUCCESS(aLoadNtStatus)) { 196 aModuleLoadInfo.CaptureBacktrace(); 197 } 198 199 // This method should only be called after a matching call to 200 // NotifyBeginDllLoad, so it is already protected against gLoaderObserver 201 // change, through gLoaderObserverOngoingLoadsCount. 202 203 // We need to notify the observer that the DLL load has ended even when 204 // |aLoadNtStatus| indicates a failure. This is to ensure that any resources 205 // acquired by the observer during OnBeginDllLoad are cleaned up. 206 gLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus, 207 std::move(aModuleLoadInfo)); 208 209 auto previousValue = gLoaderObserverOngoingLoadsCount--; 210 MOZ_RELEASE_ASSERT(previousValue); 211 if (previousValue == 1) { 212 ::RtlWakeAllConditionVariable(&gLoaderObserverNoOngoingLoadsCV); 213 } 214 } 215 216 nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName( 217 void* aSectionAddr) { 218 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); 219 220 nt::MemorySectionNameBuf buf; 221 NTSTATUS ntStatus = 222 ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName, 223 &buf, sizeof(buf), nullptr); 224 if (!NT_SUCCESS(ntStatus)) { 225 return nt::AllocatedUnicodeString(); 226 } 227 228 return nt::AllocatedUnicodeString(&buf.mSectionFileName); 229 } 230 231 nt::LoaderAPI::InitDllBlocklistOOPFnPtr 232 LoaderPrivateAPIImp::GetDllBlocklistInitFn() { 233 return &InitializeDllBlocklistOOP; 234 } 235 236 nt::LoaderAPI::HandleLauncherErrorFnPtr 237 LoaderPrivateAPIImp::GetHandleLauncherErrorFn() { 238 return &HandleLauncherError; 239 } 240 241 nt::SharedSection* LoaderPrivateAPIImp::GetSharedSection() { 242 return &gSharedSection; 243 } 244 245 nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer( 246 void* aSectionAddr) { 247 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); 248 249 nt::MemorySectionNameBuf buf; 250 NTSTATUS ntStatus = 251 ::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName, 252 &buf, sizeof(buf), nullptr); 253 if (!NT_SUCCESS(ntStatus)) { 254 return nt::MemorySectionNameBuf(); 255 } 256 257 return buf; 258 } 259 260 void LoaderPrivateAPIImp::NotifyBeginDllLoad( 261 void** aContext, PCUNICODE_STRING aRequestedDllName) { 262 nt::AutoSharedLock lock(gLoaderObserverLock); 263 ++gLoaderObserverOngoingLoadsCount; 264 gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName); 265 } 266 267 void LoaderPrivateAPIImp::NotifyBeginDllLoad( 268 ModuleLoadInfo& aModuleLoadInfo, void** aContext, 269 PCUNICODE_STRING aRequestedDllName) { 270 NotifyBeginDllLoad(aContext, aRequestedDllName); 271 aModuleLoadInfo.SetBeginLoadTimeStamp(); 272 } 273 274 void LoaderPrivateAPIImp::SetObserver(nt::LoaderObserver* aNewObserver) { 275 nt::LoaderObserver* prevLoaderObserver = nullptr; 276 277 nt::AutoExclusiveLock lock(gLoaderObserverLock); 278 while (gLoaderObserverOngoingLoadsCount) { 279 NTSTATUS status = ::RtlSleepConditionVariableSRW( 280 &gLoaderObserverNoOngoingLoadsCV, &gLoaderObserverLock, nullptr, 0); 281 MOZ_RELEASE_ASSERT(NT_SUCCESS(status) && status != STATUS_TIMEOUT); 282 } 283 284 MOZ_ASSERT(aNewObserver); 285 if (!aNewObserver) { 286 // This is unlikely, but we always want a valid observer, so use the 287 // gDefaultObserver if necessary. 288 gLoaderObserver = &gDefaultObserver; 289 return; 290 } 291 292 prevLoaderObserver = gLoaderObserver; 293 gLoaderObserver = aNewObserver; 294 295 MOZ_ASSERT(prevLoaderObserver); 296 if (!prevLoaderObserver) { 297 return; 298 } 299 300 // Now that we have a new observer, the previous observer must forward its 301 // data on to the new observer for processing. 302 prevLoaderObserver->Forward(aNewObserver); 303 } 304 305 bool LoaderPrivateAPIImp::IsDefaultObserver() const { 306 nt::AutoSharedLock lock(gLoaderObserverLock); 307 return gLoaderObserver == &gDefaultObserver; 308 } 309 310 void EnsureInitialized() { Init(); } 311 312 } // namespace freestanding 313 } // namespace mozilla