diff --git a/browser/app/nsBrowserApp.cpp b/browser/app/nsBrowserApp.cpp index b29e57a960fd..db2eb9637c57 100644 --- a/browser/app/nsBrowserApp.cpp +++ b/browser/app/nsBrowserApp.cpp @@ -25,6 +25,7 @@ #ifdef XP_WIN # include "LauncherProcessWin.h" +# include "mozilla/WindowsDllBlocklist.h" # define XRE_WANT_ENVIRON # define strcasecmp _stricmp @@ -38,7 +39,6 @@ #include "mozilla/Sprintf.h" #include "mozilla/StartupTimeline.h" -#include "mozilla/WindowsDllBlocklist.h" #include "BaseProfiler.h" #ifdef LIBFUZZER diff --git a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h index 44918aa0fffd..f9fc000f306e 100644 --- a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h +++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h @@ -7,7 +7,7 @@ #ifndef mozilla_freestanding_LoaderPrivateAPI_h #define mozilla_freestanding_LoaderPrivateAPI_h -#include "LoaderAPIInterfaces.h" +#include "mozilla/LoaderAPIInterfaces.h" namespace mozilla { namespace freestanding { diff --git a/browser/app/winlauncher/freestanding/moz.build b/browser/app/winlauncher/freestanding/moz.build index f53de1b948a5..7182ccc16066 100644 --- a/browser/app/winlauncher/freestanding/moz.build +++ b/browser/app/winlauncher/freestanding/moz.build @@ -13,11 +13,6 @@ FORCE_STATIC_LIB = True # that might call an import. NO_PGO = True -EXPORTS.mozilla += [ - 'LoaderAPIInterfaces.h', - 'ModuleLoadInfo.h', -] - UNIFIED_SOURCES += [ 'DllBlocklist.cpp', 'LoaderPrivateAPI.cpp', diff --git a/browser/app/winlauncher/moz.build b/browser/app/winlauncher/moz.build index 3aa9d38fadd9..499396b3774b 100644 --- a/browser/app/winlauncher/moz.build +++ b/browser/app/winlauncher/moz.build @@ -8,10 +8,6 @@ Library('winlauncher') FORCE_STATIC_LIB = True -EXPORTS.mozilla += [ - 'NtLoaderAPI.h', -] - UNIFIED_SOURCES += [ '/ipc/mscom/ProcessRuntime.cpp', '/widget/windows/WindowsConsole.cpp', diff --git a/ipc/app/MozillaRuntimeMain.cpp b/ipc/app/MozillaRuntimeMain.cpp index d0ab944a3007..b0a31cc23b0c 100644 --- a/ipc/app/MozillaRuntimeMain.cpp +++ b/ipc/app/MozillaRuntimeMain.cpp @@ -7,7 +7,9 @@ #include "../contentproc/plugin-container.cpp" #include "mozilla/Bootstrap.h" -#include "mozilla/WindowsDllBlocklist.h" +#if defined(XP_WIN) +# include "mozilla/WindowsDllBlocklist.h" +#endif // defined(XP_WIN) using namespace mozilla; diff --git a/ipc/mscom/mozglue/ProcessRuntimeShared.cpp b/ipc/mscom/mozglue/ProcessRuntimeShared.cpp index 57f769c07409..b9a46316de03 100644 --- a/ipc/mscom/mozglue/ProcessRuntimeShared.cpp +++ b/ipc/mscom/mozglue/ProcessRuntimeShared.cpp @@ -5,7 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/mscom/ProcessRuntimeShared.h" -#include "MozglueUtils.h" + +#include "mozilla/glue/WinUtils.h" // We allow multiple ProcessRuntime instances to exist simultaneously (even // on separate threads), but only one should be doing the process-wide diff --git a/ipc/mscom/mozglue/moz.build b/ipc/mscom/mozglue/moz.build index 131a3ff6f812..a8c497df9b01 100644 --- a/ipc/mscom/mozglue/moz.build +++ b/ipc/mscom/mozglue/moz.build @@ -10,10 +10,6 @@ EXPORTS.mozilla.mscom += [ 'ProcessRuntimeShared.h', ] -LOCAL_INCLUDES += [ - '/mozglue/build', -] - UNIFIED_SOURCES += [ 'ProcessRuntimeShared.cpp', ] diff --git a/js/xpconnect/shell/xpcshell.cpp b/js/xpconnect/shell/xpcshell.cpp index 80d69d06256a..fe65befe5ae2 100644 --- a/js/xpconnect/shell/xpcshell.cpp +++ b/js/xpconnect/shell/xpcshell.cpp @@ -8,7 +8,6 @@ #include -#include "mozilla/WindowsDllBlocklist.h" #include "mozilla/Bootstrap.h" #include "nsXULAppAPI.h" @@ -16,6 +15,8 @@ # include "xpcshellMacUtils.h" #endif #ifdef XP_WIN +# include "mozilla/WindowsDllBlocklist.h" + # include # include diff --git a/mozglue/build/UntrustedDllsHandler.cpp b/mozglue/build/UntrustedDllsHandler.cpp deleted file mode 100644 index d8617da37ca5..000000000000 --- a/mozglue/build/UntrustedDllsHandler.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "UntrustedDllsHandler.h" - -#include - -#include "mozilla/Atomics.h" -#include "mozilla/mozalloc.h" -#include "mozilla/RefPtr.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/ThreadLocal.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/Unused.h" -#include "nsWindowsHelpers.h" // For AutoCriticalSection - -namespace mozilla { -namespace glue { - -// Copies a null-terminated string. Upon error, returns nullptr. -static UniquePtr CopyString(const UniquePtr& aOther) { - if (!aOther) { - return nullptr; - } - size_t chars = wcslen(aOther.get()); - auto ret = MakeUnique(chars + 1); - if (wcsncpy_s(ret.get(), chars + 1, aOther.get(), chars)) { - return nullptr; - } - return ret; -} - -// Creates a UniquePtr from a PCUNICODE_STRING string. -// Upon error, returns nullptr. -static UniquePtr CopyString(PCUNICODE_STRING aOther) { - if (!aOther || !aOther->Buffer) { - return nullptr; - } - size_t chars = aOther->Length / sizeof(wchar_t); - auto ret = MakeUnique(chars + 1); - if (wcsncpy_s(ret.get(), chars + 1, aOther->Buffer, chars)) { - return nullptr; - } - return ret; -} - -// Basic wrapper around ::GetModuleFileNameW. -// Returns the full path of the loaded module specified by aModuleBase. -// Upon error, returns nullptr. -static UniquePtr GetModuleFullPath(uintptr_t aModuleBase) { - size_t allocated = MAX_PATH; - auto ret = MakeUnique(allocated); - size_t len; - while (true) { - len = (size_t)::GetModuleFileNameW((HMODULE)aModuleBase, ret.get(), - allocated); - if (!len) { - return nullptr; - } - if (len == allocated && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - allocated *= 2; - ret = MakeUnique(allocated); - continue; - } - break; - } - // The buffer may much bigger than needed. Return an efficiently-allocated - // buffer. - return CopyString(ret); -} - -// To track call depth and recursively-loaded modules, we must store this data -// in thread local storage. -class TLSData { - public: - Vector mModulesLoaded; - int mCallDepth = 0; -}; - -static MOZ_THREAD_LOCAL(TLSData*) sTlsData; - -// This singleton class does the underlying work for UntrustedDllsHandler -class UntrustedDllsHandlerImpl { - // Refcounting gives us a way to synchronize call lifetime vs object lifetime. - // We don't have access to NS_INLINE_DECL_THREADSAFE_REFCOUNTING from mozglue, - // but it's easy to roll our own. - Atomic mRefCnt; - - // In order to prevent sInstance from being "woken back up" after it's been - // cleared on shutdown, this will let us know if sInstance is empty because - // it's not initialized yet, or because it's been cleared on shutdown. - static Atomic sInstanceHasBeenSet; - - // Singleton reference - static StaticRefPtr sInstance; - - // Holds a list of module load events. This gets emptied upon calling - // UntrustedDllsHandler::TakePendingEvents - Vector mModuleLoadEvents; - - // Holds a list of module full paths that we've already handled, so we can - // skip duplicates. - Vector> mModuleHistory; - - // This lock protects gModuleLoadEvents and gModuleHistory. - // - // You must only make trivial, loader-lock-friendly calls within the lock. We - // cannot risk re-entering the loader at this point. - CRITICAL_SECTION mDataLock; - - UntrustedDllsHandlerImpl() { InitializeCriticalSection(&mDataLock); } - - ~UntrustedDllsHandlerImpl() { - { // Scope for lock - // Ensure pending ops are complete. - AutoCriticalSection lock(&mDataLock); - } - DeleteCriticalSection(&mDataLock); - } - - public: - static RefPtr GetInstance() { - if (sInstanceHasBeenSet) { - return sInstance; - } - - sInstance = new UntrustedDllsHandlerImpl(); - sInstanceHasBeenSet = true; - return sInstance; - } - - static void Shutdown() { sInstance = nullptr; } - - int32_t AddRef() { return ++mRefCnt; } - - int32_t Release() { - int32_t ret = --mRefCnt; - if (!ret) { - delete this; - } - return ret; - } - - // Called after a successful module load at the top level. Now we are safe - // to package up the event and save for later processing. - void OnAfterTopLevelModuleLoad() { - // Hold a reference to ensure we don't get deleted during this call. - RefPtr refHolder(this); - if (!refHolder) { - return; - } - - ModuleLoadEvent thisEvent; - - TLSData* tlsData = sTlsData.get(); - if (!tlsData) { - return; - } - - { // Scope for lock - // Lock around gModuleHistory to prune out modules we've handled before. - // Only trivial calls allowed during lock (don't invoke the loader) - AutoCriticalSection lock(&mDataLock); - - // There will rarely be more than a couple items in - // tlsData->mModulesLoaded, so this is efficient enough. - for (auto& module : tlsData->mModulesLoaded) { - if (module.mFullPath && wcslen(module.mFullPath.get())) { - bool foundInHistory = false; - for (auto& h : mModuleHistory) { - if (!wcsicmp(h.get(), module.mFullPath.get())) { - foundInHistory = true; - break; - } - } - if (foundInHistory) { - continue; - } - Unused << mModuleHistory.append(CopyString(module.mFullPath)); - } - Unused << thisEvent.mModules.emplaceBack(std::move(module)); - } - } - - tlsData->mModulesLoaded.clear(); - - if (thisEvent.mModules.empty()) { - return; // All modules have been filtered out; nothing further to do. - } - - thisEvent.mThreadID = GetCurrentThreadId(); - - TimeStamp processCreation = TimeStamp::ProcessCreation(); - TimeDuration td = TimeStamp::Now() - processCreation; - thisEvent.mProcessUptimeMS = (uint64_t)td.ToMilliseconds(); - - static const uint32_t kMaxFrames = 500; - auto frames = MakeUnique(kMaxFrames); - - // Setting FramesToSkip to 1 so the caller will see itself as the top frame. - USHORT frameCount = - CaptureStackBackTrace(1, kMaxFrames, frames.get(), nullptr); - if (thisEvent.mStack.reserve(frameCount)) { - for (size_t i = 0; i < frameCount; ++i) { - Unused << thisEvent.mStack.append((uintptr_t)frames[i]); - } - } - - // Lock in order to store the new event. - // Only trivial calls allowed during lock (don't invoke the loader) - AutoCriticalSection lock(&mDataLock); - Unused << mModuleLoadEvents.emplaceBack(std::move(thisEvent)); - } - - // Returns true if aOut is no longer empty. - bool TakePendingEvents( - Vector& aOut) { - // Hold a reference to ensure we don't get deleted during this call. - RefPtr refHolder(this); - if (!refHolder) { - return false; - } - - AutoCriticalSection lock(&mDataLock); - mModuleLoadEvents.swap(aOut); - return !aOut.empty(); - } -}; - -Atomic UntrustedDllsHandlerImpl::sInstanceHasBeenSet; -StaticRefPtr UntrustedDllsHandlerImpl::sInstance; - -/* static */ -void UntrustedDllsHandler::Init() { Unused << sTlsData.init(); } - -#ifdef DEBUG -/* static */ -void UntrustedDllsHandler::Shutdown() { UntrustedDllsHandlerImpl::Shutdown(); } -#endif // DEBUG - -/* static */ -void UntrustedDllsHandler::EnterLoaderCall() { - if (!sTlsData.initialized()) { - return; - } - if (!sTlsData.get()) { - sTlsData.set(new TLSData()); - } - sTlsData.get()->mCallDepth++; -} - -/* static */ -void UntrustedDllsHandler::ExitLoaderCall() { - if (!sTlsData.initialized()) { - return; - } - if (!--(sTlsData.get()->mCallDepth)) { - delete sTlsData.get(); - sTlsData.set(nullptr); - } -} - -/* static */ -void UntrustedDllsHandler::OnAfterModuleLoad(uintptr_t aBaseAddr, - PUNICODE_STRING aLdrModuleName, - double aLoadDurationMS) { - RefPtr p(UntrustedDllsHandlerImpl::GetInstance()); - if (!p) { - return; - } - - if (!sTlsData.initialized()) { - return; - } - TLSData* tlsData = sTlsData.get(); - if (!tlsData) { - return; - } - - ModuleLoadEvent::ModuleInfo moduleInfo; - moduleInfo.mLdrName = CopyString(aLdrModuleName); - moduleInfo.mBase = aBaseAddr; - moduleInfo.mFullPath = GetModuleFullPath(aBaseAddr); - moduleInfo.mLoadDurationMS = aLoadDurationMS; - - Unused << tlsData->mModulesLoaded.emplaceBack(std::move(moduleInfo)); - - if (tlsData->mCallDepth > 1) { - // Recursive call; bail and wait until top-level call can proceed. - return; - } - - p->OnAfterTopLevelModuleLoad(); -} - -/* static */ -bool UntrustedDllsHandler::TakePendingEvents( - Vector& aOut) { - RefPtr p(UntrustedDllsHandlerImpl::GetInstance()); - if (!p) { - return false; - } - return p->TakePendingEvents(aOut); -} - -} // namespace glue -} // namespace mozilla diff --git a/mozglue/build/UntrustedDllsHandler.h b/mozglue/build/UntrustedDllsHandler.h deleted file mode 100644 index d2daa751bd30..000000000000 --- a/mozglue/build/UntrustedDllsHandler.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_glue_UntrustedDllsHandler_h -#define mozilla_glue_UntrustedDllsHandler_h - -#include -#include // For PUNICODE_STRING - -#include "mozilla/Attributes.h" -#include "mozilla/glue/WindowsDllServices.h" // For ModuleLoadEvent -#include "mozilla/Vector.h" - -namespace mozilla { -namespace glue { - -// This class examines successful module loads, converts them to a -// glue::ModuleLoadEvent, and stores them for consumption by the caller. -// In the process, we capture data about the event that is later used to -// evaluate trustworthiness of the module. -class UntrustedDllsHandler { - public: - static void Init(); - -#ifdef DEBUG - static void Shutdown(); -#endif // DEBUG - - /** - * To prevent issues with re-entrancy, we only capture module load events at - * the top-level call. Recursive module loads get bundled with the top-level - * module load. In order to do that, we must track when LdrLoadDLL() enters - * and exits, to detect recursion. - * - * EnterLoaderCall() must be called at the top of LdrLoadDLL(), and - * ExitLoaderCall() must be called when LdrLoadDLL() exits. - * - * These methods may be called even outside of Init() / Shutdown(). - */ - static void EnterLoaderCall(); - static void ExitLoaderCall(); - - /** - * OnAfterModuleLoad should be called between calls to EnterLoaderCall() and - * ExitLoaderCall(). This handles the successful module load and records it - * internally if needed. To handle the resulting recorded event, call - * TakePendingEvents(). - * - * This method may be called even outside of Init() / Shutdown(). - */ - static void OnAfterModuleLoad(uintptr_t aBaseAddr, - PUNICODE_STRING aLdrModuleName, - double aLoadDurationMS); - - /** - * Call TakePendingEvents to get any events that have been recorded since - * the last call to TakePendingEvents(). - * - * This method may be called even outside of Init() / Shutdown(). - * - * @param aOut [out] Receives a list of events. - * @return true if aOut now contains elements. - */ - static bool TakePendingEvents( - Vector& aOut); -}; - -} // namespace glue -} // namespace mozilla - -#endif // mozilla_glue_UntrustedDllsHandler_h diff --git a/mozglue/build/moz.build b/mozglue/build/moz.build index 908c9bb588a0..48c115746b74 100644 --- a/mozglue/build/moz.build +++ b/mozglue/build/moz.build @@ -58,48 +58,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT']: '/memory/build', ] - SOURCES += [ - 'Authenticode.cpp', - 'UntrustedDllsHandler.cpp', - 'WindowsDllBlocklist.cpp', - ] - - OS_LIBS += [ - 'crypt32', - 'version', - 'wintrust', - ] - DELAYLOAD_DLLS += [ - 'crypt32.dll', - 'wintrust.dll', - ] - EXPORTS.mozilla += [ - 'Authenticode.h', - 'WindowsDllBlocklistCommon.h', - ] - EXPORTS.mozilla.glue += [ - 'WindowsDllServices.h', - ] - - # Generate DLL Blocklists - blocklist_header_types = ['A11y', 'Launcher', 'Legacy', 'Test'] - blocklist_file_leaf_tpl = 'WindowsDllBlocklist{0}Defs.h' - blocklist_files = tuple([blocklist_file_leaf_tpl.format(type) - for type in blocklist_header_types]) - GENERATED_FILES += [ - blocklist_files - ] - blocklist_defs = GENERATED_FILES[blocklist_files] - blocklist_defs.script = 'gen_dll_blocklist_defs.py:gen_blocklists' - blocklist_defs.inputs = ['WindowsDllBlocklistDefs.in'] - EXPORTS.mozilla += ['!' + hdr for hdr in blocklist_files] - EXPORTS.mozilla += [ 'arm.h', 'mips.h', 'ppc.h', 'SSE.h', - 'WindowsDllBlocklist.h', ] if CONFIG['CPU_ARCH'].startswith('x86'): diff --git a/mozglue/build/Authenticode.cpp b/mozglue/dllservices/Authenticode.cpp similarity index 100% rename from mozglue/build/Authenticode.cpp rename to mozglue/dllservices/Authenticode.cpp diff --git a/mozglue/build/Authenticode.h b/mozglue/dllservices/Authenticode.h similarity index 100% rename from mozglue/build/Authenticode.h rename to mozglue/dllservices/Authenticode.h diff --git a/browser/app/winlauncher/freestanding/LoaderAPIInterfaces.h b/mozglue/dllservices/LoaderAPIInterfaces.h similarity index 96% rename from browser/app/winlauncher/freestanding/LoaderAPIInterfaces.h rename to mozglue/dllservices/LoaderAPIInterfaces.h index 79d914fb04d2..63b56465a3b4 100644 --- a/browser/app/winlauncher/freestanding/LoaderAPIInterfaces.h +++ b/mozglue/dllservices/LoaderAPIInterfaces.h @@ -4,8 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#ifndef mozilla_LoaderAPIInternal_h -#define mozilla_LoaderAPIInternal_h +#ifndef mozilla_LoaderAPIInterfaces_h +#define mozilla_LoaderAPIInterfaces_h #include "mozilla/ModuleLoadInfo.h" @@ -97,4 +97,4 @@ class NS_NO_VTABLE LoaderAPI { } // namespace nt } // namespace mozilla -#endif // mozilla_LoaderAPIInternal_h +#endif // mozilla_LoaderAPIInterfaces_h diff --git a/mozglue/dllservices/LoaderObserver.cpp b/mozglue/dllservices/LoaderObserver.cpp new file mode 100644 index 000000000000..27878cfef363 --- /dev/null +++ b/mozglue/dllservices/LoaderObserver.cpp @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "LoaderObserver.h" + +#include "mozilla/AutoProfilerLabel.h" +#include "mozilla/glue/WindowsUnicode.h" +#include "mozilla/StackWalk_windows.h" + +namespace { + +struct LoadContext { + LoadContext(mozilla::ProfilerLabel&& aLabel, + mozilla::UniquePtr&& aDynamicStringStorage) + : mProfilerLabel(std::move(aLabel)), + mDynamicStringStorage(std::move(aDynamicStringStorage)) {} + mozilla::ProfilerLabel mProfilerLabel; + mozilla::UniquePtr mDynamicStringStorage; +}; + +} // anonymous namespace + +namespace mozilla { + +extern glue::Win32SRWLock gDllServicesLock; +extern glue::detail::DllServicesBase* gDllServices; + +namespace glue { + +void LoaderObserver::OnBeginDllLoad(void** aContext, + PCUNICODE_STRING aRequestedDllName) { + MOZ_ASSERT(aContext); + if (IsProfilerPresent()) { + UniquePtr utf8RequestedDllName(WideToUTF8(aRequestedDllName)); + const char* dynamicString = utf8RequestedDllName.get(); + *aContext = new LoadContext( + ProfilerLabelBegin("mozilla::glue::LoaderObserver::OnBeginDllLoad", + dynamicString, &aContext), + std::move(utf8RequestedDllName)); + } + +#ifdef _M_AMD64 + // Prevent the stack walker from suspending this thread when LdrLoadDll + // holds the RtlLookupFunctionEntry lock. + SuppressStackWalking(); +#endif +} + +bool LoaderObserver::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) { + // Currently unsupported + return false; +} + +void LoaderObserver::OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) { +#ifdef _M_AMD64 + DesuppressStackWalking(); +#endif + + MOZ_ASSERT_IF(IsProfilerPresent(), aContext); + UniquePtr loadContext(static_cast(aContext)); + if (loadContext && IsValidProfilerLabel(loadContext->mProfilerLabel)) { + ProfilerLabelEnd(loadContext->mProfilerLabel); + } + + if (!NT_SUCCESS(aNtStatus) || !aModuleLoadInfo.WasMapped()) { + return; + } + + { // Scope for lock + AutoSharedLock lock(gDllServicesLock); + if (gDllServices) { + gDllServices->DispatchDllLoadNotification(std::move(aModuleLoadInfo)); + return; + } + } + + // No dll services, save for later + AutoExclusiveLock lock(mLock); + if (!mModuleLoads) { + mModuleLoads = new ModuleLoadInfoVec(); + } + + Unused << mModuleLoads->emplaceBack( + std::forward(aModuleLoadInfo)); +} + +void LoaderObserver::Forward(nt::LoaderObserver* aNext) { + MOZ_ASSERT_UNREACHABLE( + "This implementation does not forward to any more " + "nt::LoaderObserver objects"); +} + +void LoaderObserver::Forward(detail::DllServicesBase* aNext) { + MOZ_ASSERT(aNext); + if (!aNext) { + return; + } + + ModuleLoadInfoVec* moduleLoads = nullptr; + + { // Scope for lock + AutoExclusiveLock lock(mLock); + moduleLoads = mModuleLoads; + mModuleLoads = nullptr; + } + + if (!moduleLoads) { + return; + } + + aNext->DispatchModuleLoadBacklogNotification(std::move(*moduleLoads)); + delete moduleLoads; +} + +void LoaderObserver::Clear() { + ModuleLoadInfoVec* moduleLoads = nullptr; + + { // Scope for lock + AutoExclusiveLock lock(mLock); + moduleLoads = mModuleLoads; + mModuleLoads = nullptr; + } + + delete moduleLoads; +} + +void LoaderObserver::OnForward(ModuleLoadInfoVec&& aInfo) { + AutoExclusiveLock lock(mLock); + if (!mModuleLoads) { + mModuleLoads = new ModuleLoadInfoVec(); + } + + MOZ_ASSERT(mModuleLoads->empty()); + if (mModuleLoads->empty()) { + *mModuleLoads = std::move(aInfo); + } else { + // This should not happen, but we can handle it + for (auto&& item : aInfo) { + Unused << mModuleLoads->append(std::move(item)); + } + } +} + +} // namespace glue +} // namespace mozilla diff --git a/mozglue/dllservices/LoaderObserver.h b/mozglue/dllservices/LoaderObserver.h new file mode 100644 index 000000000000..01f55f3f3c2c --- /dev/null +++ b/mozglue/dllservices/LoaderObserver.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_glue_LoaderObserver_h +#define mozilla_glue_LoaderObserver_h + +#include "mozilla/Attributes.h" +#include "mozilla/LoaderAPIInterfaces.h" +#include "mozilla/glue/WindowsDllServices.h" +#include "mozilla/glue/WinUtils.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla { +namespace glue { + +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderObserver final + : public nt::LoaderObserver { + public: + constexpr LoaderObserver() : mModuleLoads(nullptr) {} + + void OnBeginDllLoad(void** aContext, + PCUNICODE_STRING aPreliminaryDllName) final; + bool SubstituteForLSP(PCUNICODE_STRING aLspLeafName, + PHANDLE aOutHandle) final; + void OnEndDllLoad(void* aContext, NTSTATUS aNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) final; + void Forward(nt::LoaderObserver* aNext) final; + void OnForward(ModuleLoadInfoVec&& aInfo) final; + + void Forward(mozilla::glue::detail::DllServicesBase* aSvc); + void Clear(); + + private: + Win32SRWLock mLock; + ModuleLoadInfoVec* mModuleLoads; +}; + +} // namespace glue +} // namespace mozilla + +#endif // mozilla_glue_LoaderObserver_h diff --git a/mozglue/dllservices/ModuleLoadFrame.cpp b/mozglue/dllservices/ModuleLoadFrame.cpp new file mode 100644 index 000000000000..f0a9225f930b --- /dev/null +++ b/mozglue/dllservices/ModuleLoadFrame.cpp @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ModuleLoadFrame.h" +#include "mozilla/NativeNt.h" +#include "mozilla/UniquePtr.h" +#include "NtLoaderAPI.h" + +#include + +#include "WindowsFallbackLoaderAPI.h" + +static bool IsNullTerminated(PCUNICODE_STRING aStr) { + return aStr && (aStr->MaximumLength >= (aStr->Length + sizeof(WCHAR))) && + aStr->Buffer && aStr->Buffer[aStr->Length / sizeof(WCHAR)] == 0; +} + +static mozilla::FallbackLoaderAPI gFallbackLoaderAPI; + +namespace mozilla { +namespace glue { + +nt::LoaderAPI* ModuleLoadFrame::sLoaderAPI; + +using GetNtLoaderAPIFn = decltype(&mozilla::GetNtLoaderAPI); + +/* static */ +void ModuleLoadFrame::StaticInit(nt::LoaderObserver* aNewObserver) { + const auto pGetNtLoaderAPI = reinterpret_cast( + ::GetProcAddress(::GetModuleHandleW(nullptr), "GetNtLoaderAPI")); + if (!pGetNtLoaderAPI) { + // This case occurs in processes other than firefox.exe that do not contain + // the launcher process blocklist. + gFallbackLoaderAPI.SetObserver(aNewObserver); + sLoaderAPI = &gFallbackLoaderAPI; + return; + } + + sLoaderAPI = pGetNtLoaderAPI(aNewObserver); +} + +ModuleLoadFrame::ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName) + : mAlreadyLoaded(false), + mContext(nullptr), + mDllLoadStatus(STATUS_UNSUCCESSFUL), + mLoadInfo(sLoaderAPI->ConstructAndNotifyBeginDllLoad(&mContext, + aRequestedDllName)) { + if (!aRequestedDllName) { + return; + } + + UniquePtr nameBuf; + const WCHAR* name = nullptr; + + if (IsNullTerminated(aRequestedDllName)) { + name = aRequestedDllName->Buffer; + } else { + USHORT charLenExclNul = aRequestedDllName->Length / sizeof(WCHAR); + USHORT charLenInclNul = charLenExclNul + 1; + nameBuf = MakeUnique(charLenInclNul); + if (!wcsncpy_s(nameBuf.get(), charLenInclNul, aRequestedDllName->Buffer, + charLenExclNul)) { + name = nameBuf.get(); + } + } + + mAlreadyLoaded = name && !!::GetModuleHandleW(name); +} + +ModuleLoadFrame::~ModuleLoadFrame() { + sLoaderAPI->NotifyEndDllLoad(mContext, mDllLoadStatus, std::move(mLoadInfo)); +} + +void ModuleLoadFrame::SetLoadStatus(NTSTATUS aNtStatus, HANDLE aHandle) { + mDllLoadStatus = aNtStatus; + void* baseAddr = mozilla::nt::PEHeaders::HModuleToBaseAddr( + reinterpret_cast(aHandle)); + mLoadInfo.mBaseAddr = baseAddr; + if (!mAlreadyLoaded) { + mLoadInfo.mSectionName = sLoaderAPI->GetSectionName(baseAddr); + } +} + +} // namespace glue +} // namespace mozilla diff --git a/mozglue/dllservices/ModuleLoadFrame.h b/mozglue/dllservices/ModuleLoadFrame.h new file mode 100644 index 000000000000..de8ef04f09ce --- /dev/null +++ b/mozglue/dllservices/ModuleLoadFrame.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_glue_ModuleLoadFrame_h +#define mozilla_glue_ModuleLoadFrame_h + +#include "mozilla/Attributes.h" +#include "mozilla/LoaderAPIInterfaces.h" + +namespace mozilla { +namespace glue { + +class MOZ_RAII ModuleLoadFrame final { + public: + explicit ModuleLoadFrame(PCUNICODE_STRING aRequestedDllName); + ~ModuleLoadFrame(); + + void SetLoadStatus(NTSTATUS aNtStatus, HANDLE aHandle); + + ModuleLoadFrame(const ModuleLoadFrame&) = delete; + ModuleLoadFrame(ModuleLoadFrame&&) = delete; + ModuleLoadFrame& operator=(const ModuleLoadFrame&) = delete; + ModuleLoadFrame& operator=(ModuleLoadFrame&&) = delete; + + static void StaticInit(nt::LoaderObserver* aNewObserver); + + private: + bool mAlreadyLoaded; + void* mContext; + NTSTATUS mDllLoadStatus; + ModuleLoadInfo mLoadInfo; + + private: + static nt::LoaderAPI* sLoaderAPI; +}; + +} // namespace glue +} // namespace mozilla + +#endif // mozilla_glue_ModuleLoadFrame_h diff --git a/browser/app/winlauncher/freestanding/ModuleLoadInfo.h b/mozglue/dllservices/ModuleLoadInfo.h similarity index 100% rename from browser/app/winlauncher/freestanding/ModuleLoadInfo.h rename to mozglue/dllservices/ModuleLoadInfo.h diff --git a/browser/app/winlauncher/NtLoaderAPI.h b/mozglue/dllservices/NtLoaderAPI.h similarity index 100% rename from browser/app/winlauncher/NtLoaderAPI.h rename to mozglue/dllservices/NtLoaderAPI.h diff --git a/mozglue/build/WindowsDllBlocklist.cpp b/mozglue/dllservices/WindowsDllBlocklist.cpp similarity index 75% rename from mozglue/build/WindowsDllBlocklist.cpp rename to mozglue/dllservices/WindowsDllBlocklist.cpp index ae25de9b090e..25300c0a1705 100644 --- a/mozglue/build/WindowsDllBlocklist.cpp +++ b/mozglue/dllservices/WindowsDllBlocklist.cpp @@ -14,8 +14,6 @@ #include "Authenticode.h" #include "BaseProfiler.h" #include "CrashAnnotations.h" -#include "MozglueUtils.h" -#include "UntrustedDllsHandler.h" #include "nsAutoPtr.h" #include "nsWindowsDllInterceptor.h" #include "mozilla/CmdLineAndEnvUtils.h" @@ -32,15 +30,25 @@ #include "mozilla/AutoProfilerLabel.h" #include "mozilla/glue/Debug.h" #include "mozilla/glue/WindowsDllServices.h" +#include "mozilla/glue/WinUtils.h" + +// Start new implementation +#include "LoaderObserver.h" +#include "ModuleLoadFrame.h" +#include "mozilla/glue/WindowsUnicode.h" + +namespace mozilla { + +glue::Win32SRWLock gDllServicesLock; +glue::detail::DllServicesBase* gDllServices; + +} // namespace mozilla using namespace mozilla; using CrashReporter::Annotation; using CrashReporter::AnnotationToString; -static glue::Win32SRWLock gDllServicesLock; -static glue::detail::DllServicesBase* gDllServices; - #define DLL_BLOCKLIST_ENTRY(name, ...) {name, __VA_ARGS__}, #define DLL_BLOCKLIST_STRING_TYPE const char* #include "mozilla/WindowsDllBlocklistLegacyDefs.h" @@ -53,15 +61,6 @@ static bool sBlocklistInitAttempted; static bool sBlocklistInitFailed; static bool sUser32BeforeBlocklist; -// This feature is enabled only on NIGHTLY, only for the main process. -inline static bool IsUntrustedDllsHandlerEnabled() { -#ifdef NIGHTLY_BUILD - return !(sInitFlags & eDllBlocklistInitFlagIsChildProcess); -#else - return false; -#endif -} - typedef MOZ_NORETURN_PTR void(__fastcall* BaseThreadInitThunk_func)( BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam); static WindowsDllInterceptor::FuncHookType @@ -337,16 +336,6 @@ static wchar_t* lastslash(wchar_t* s, int len) { static NTSTATUS NTAPI patched_LdrLoadDll(PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle) { - if (IsUntrustedDllsHandlerEnabled()) { - glue::UntrustedDllsHandler::EnterLoaderCall(); - } - // Warning: this must be at the top function scope. - auto exitLoaderCallScopeExit = MakeScopeExit([]() { - if (IsUntrustedDllsHandlerEnabled()) { - glue::UntrustedDllsHandler::ExitLoaderCall(); - } - }); - // We have UCS2 (UTF16?), we want ASCII, but we also just want the filename // portion #define DLLNAME_MAX 128 @@ -516,63 +505,35 @@ continue_loading: moduleFileName->Buffer); #endif - // A few DLLs such as xul.dll and nss3.dll get loaded before mozglue's - // AutoProfilerLabel is initialized, and this is a no-op in those cases. But - // the vast majority of DLLs do get labelled here. - AutoProfilerLabel label("WindowsDllBlocklist::patched_LdrLoadDll", dllName); - -#ifdef _M_AMD64 - // Prevent the stack walker from suspending this thread when LdrLoadDll - // holds the RtlLookupFunctionEntry lock. - AutoSuppressStackWalking suppress; -#endif + glue::ModuleLoadFrame loadFrame(moduleFileName); NTSTATUS ret; HANDLE myHandle; - if (IsUntrustedDllsHandlerEnabled()) { - TimeStamp loadStart = TimeStamp::Now(); - ret = stub_LdrLoadDll(filePath, flags, moduleFileName, &myHandle); - TimeStamp loadEnd = TimeStamp::Now(); - - if (NT_SUCCESS(ret)) { - double loadDurationMS = (loadEnd - loadStart).ToMilliseconds(); - // Win32 HMODULEs use the bottom two bits as flags. Ensure those bits are - // cleared so we're left with the base address value. - glue::UntrustedDllsHandler::OnAfterModuleLoad( - (uintptr_t)myHandle & ~(uintptr_t)3, moduleFileName, loadDurationMS); - glue::AutoSharedLock lock(gDllServicesLock); - if (gDllServices) { - Vector events; - if (glue::UntrustedDllsHandler::TakePendingEvents(events)) { - gDllServices->NotifyUntrustedModuleLoads(events); - } - } - } - } else { - ret = stub_LdrLoadDll(filePath, flags, moduleFileName, &myHandle); - } + ret = stub_LdrLoadDll(filePath, flags, moduleFileName, &myHandle); if (handle) { *handle = myHandle; } + loadFrame.SetLoadStatus(ret, myHandle); + return ret; } #if defined(NIGHTLY_BUILD) // Map of specific thread proc addresses we should block. In particular, // LoadLibrary* APIs which indicate DLL injection -static mozilla::Vector* gStartAddressesToBlock; -#endif +static void* gStartAddressesToBlock[4]; +#endif // defined(NIGHTLY_BUILD) static bool ShouldBlockThread(void* aStartAddress) { // Allows crashfirefox.exe to continue to work. Also if your threadproc is // null, this crash is intentional. - if (aStartAddress == 0) return false; + if (aStartAddress == nullptr) return false; #if defined(NIGHTLY_BUILD) - for (auto p : *gStartAddressesToBlock) { + for (auto p : gStartAddressesToBlock) { if (p == aStartAddress) { return true; } @@ -608,56 +569,32 @@ static WindowsDllInterceptor Kernel32Intercept; static void GetNativeNtBlockSetWriter(); +static glue::LoaderObserver gMozglueLoaderObserver; + MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) { if (sBlocklistInitAttempted) { return; } + sBlocklistInitAttempted = true; + sInitFlags = aInitFlags; - if (sInitFlags & eDllBlocklistInitFlagWasBootstrapped) { - GetNativeNtBlockSetWriter(); + glue::ModuleLoadFrame::StaticInit(&gMozglueLoaderObserver); + +#ifdef _M_AMD64 + if (!IsWin8OrLater()) { + Kernel32Intercept.Init("kernel32.dll"); + + // The crash that this hook works around is only seen on Win7. + stub_RtlInstallFunctionTableCallback.Set( + Kernel32Intercept, "RtlInstallFunctionTableCallback", + &patched_RtlInstallFunctionTableCallback); } - - sBlocklistInitAttempted = true; -#if defined(NIGHTLY_BUILD) - gStartAddressesToBlock = new mozilla::Vector; #endif - if (IsUntrustedDllsHandlerEnabled()) { -#ifdef ENABLE_TESTS - // Check whether we are running as an xpcshell test. - if (mozilla::EnvHasValue("XPCSHELL_TEST_PROFILE_DIR")) { - // For xpcshell tests, load this untrusted DLL early enough that the - // untrusted module evaluator counts it as a startup module. - // It is located in the current directory; the full path must be specified - // or LoadLibrary() fails during xpcshell tests with ERROR_MOD_NOT_FOUND. - - // This buffer will hold current directory + dll name - wchar_t dllFullPath[MAX_PATH] = {}; - static const wchar_t kTestDllName[] = L"\\untrusted-startup-test-dll.dll"; - - // The amount of the buffer available to store the current directory, - // leaving room for the dll name. - static const DWORD kBufferDirLen = - ArrayLength(dllFullPath) - ArrayLength(kTestDllName); - - DWORD ret = ::GetCurrentDirectoryW(kBufferDirLen, dllFullPath); - if ((ret > kBufferDirLen) || !ret) { - // Buffer too small or the call failed - printf_stderr("Unable to load %S; GetCurrentDirectoryW failed: %lu", - kTestDllName, GetLastError()); - } else { - wcscat_s(dllFullPath, kTestDllName); - HMODULE hTestDll = ::LoadLibraryW(dllFullPath); - if (!hTestDll) { - printf_stderr("Unable to load %S; LoadLibraryW failed: %lu", - kTestDllName, GetLastError()); - } - } - } -#endif - - glue::UntrustedDllsHandler::Init(); + if (aInitFlags & eDllBlocklistInitFlagWasBootstrapped) { + GetNativeNtBlockSetWriter(); + return; } // There are a couple of exceptional cases where we skip user32.dll check. @@ -712,15 +649,6 @@ MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) { Kernel32Intercept.Init("kernel32.dll"); -#ifdef _M_AMD64 - if (!IsWin8OrLater()) { - // The crash that this hook works around is only seen on Win7. - stub_RtlInstallFunctionTableCallback.Set( - Kernel32Intercept, "RtlInstallFunctionTableCallback", - &patched_RtlInstallFunctionTableCallback); - } -#endif - // Bug 1361410: WRusr.dll will overwrite our hook and cause a crash. // Workaround: If we detect WRusr.dll, don't hook. if (!GetModuleHandleW(L"WRusr.dll")) { @@ -740,34 +668,22 @@ MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags) { void* pProc; pProc = (void*)GetProcAddress(hKernel, "LoadLibraryA"); - if (pProc) { - Unused << gStartAddressesToBlock->append(pProc); - } + gStartAddressesToBlock[0] = pProc; pProc = (void*)GetProcAddress(hKernel, "LoadLibraryW"); - if (pProc) { - Unused << gStartAddressesToBlock->append(pProc); - } + gStartAddressesToBlock[1] = pProc; pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExA"); - if (pProc) { - Unused << gStartAddressesToBlock->append(pProc); - } + gStartAddressesToBlock[2] = pProc; pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExW"); - if (pProc) { - Unused << gStartAddressesToBlock->append(pProc); - } + gStartAddressesToBlock[3] = pProc; } #endif } #ifdef DEBUG -MFBT_API void DllBlocklist_Shutdown() { - if (IsUntrustedDllsHandlerEnabled()) { - glue::UntrustedDllsHandler::Shutdown(); - } -} +MFBT_API void DllBlocklist_Shutdown() {} #endif // DEBUG static void WriteAnnotation(HANDLE aFile, Annotation aAnnotation, @@ -819,63 +735,6 @@ MFBT_API bool DllBlocklist_CheckStatus() { // This section is for DLL Services // ============================================================================ -// These types are documented on MSDN but not provided in any SDK headers - -enum DllNotificationReason { - LDR_DLL_NOTIFICATION_REASON_LOADED = 1, - LDR_DLL_NOTIFICATION_REASON_UNLOADED = 2 -}; - -typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { - ULONG Flags; // Reserved. - PCUNICODE_STRING FullDllName; // The full path name of the DLL module. - PCUNICODE_STRING BaseDllName; // The base file name of the DLL module. - PVOID DllBase; // A pointer to the base address for the DLL in memory. - ULONG SizeOfImage; // The size of the DLL image, in bytes. -} LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; - -typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA { - ULONG Flags; // Reserved. - PCUNICODE_STRING FullDllName; // The full path name of the DLL module. - PCUNICODE_STRING BaseDllName; // The base file name of the DLL module. - PVOID DllBase; // A pointer to the base address for the DLL in memory. - ULONG SizeOfImage; // The size of the DLL image, in bytes. -} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; - -typedef union _LDR_DLL_NOTIFICATION_DATA { - LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; - LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; -} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; - -typedef const LDR_DLL_NOTIFICATION_DATA* PCLDR_DLL_NOTIFICATION_DATA; - -typedef VOID(CALLBACK* PLDR_DLL_NOTIFICATION_FUNCTION)( - ULONG aReason, PCLDR_DLL_NOTIFICATION_DATA aNotificationData, - PVOID aContext); - -NTSTATUS NTAPI LdrRegisterDllNotification( - ULONG aFlags, PLDR_DLL_NOTIFICATION_FUNCTION aCallback, PVOID aContext, - PVOID* aCookie); - -static PVOID gNotificationCookie; - -static VOID CALLBACK DllLoadNotification( - ULONG aReason, PCLDR_DLL_NOTIFICATION_DATA aNotificationData, - PVOID aContext) { - if (aReason != LDR_DLL_NOTIFICATION_REASON_LOADED) { - // We don't care about unloads - return; - } - - glue::AutoSharedLock lock(gDllServicesLock); - if (!gDllServices) { - return; - } - - PCUNICODE_STRING fullDllName = aNotificationData->Loaded.FullDllName; - gDllServices->DispatchDllLoadNotification(fullDllName); -} - namespace mozilla { Authenticode* GetAuthenticode(); } // namespace mozilla @@ -885,29 +744,10 @@ MFBT_API void DllBlocklist_SetFullDllServices( glue::AutoExclusiveLock lock(gDllServicesLock); if (aSvc) { aSvc->SetAuthenticodeImpl(GetAuthenticode()); - - if (!gNotificationCookie) { - auto pLdrRegisterDllNotification = - reinterpret_cast( - ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), - "LdrRegisterDllNotification")); - - MOZ_DIAGNOSTIC_ASSERT(pLdrRegisterDllNotification); - - mozilla::DebugOnly ntStatus = pLdrRegisterDllNotification( - 0, &DllLoadNotification, nullptr, &gNotificationCookie); - MOZ_ASSERT(NT_SUCCESS(ntStatus)); - } + gMozglueLoaderObserver.Forward(aSvc); } gDllServices = aSvc; - - if (IsUntrustedDllsHandlerEnabled() && gDllServices) { - Vector events; - if (glue::UntrustedDllsHandler::TakePendingEvents(events)) { - gDllServices->NotifyUntrustedModuleLoads(events); - } - } } MFBT_API void DllBlocklist_SetBasicDllServices( @@ -917,4 +757,5 @@ MFBT_API void DllBlocklist_SetBasicDllServices( } aSvc->SetAuthenticodeImpl(GetAuthenticode()); + gMozglueLoaderObserver.Clear(); } diff --git a/mozglue/build/WindowsDllBlocklist.h b/mozglue/dllservices/WindowsDllBlocklist.h similarity index 100% rename from mozglue/build/WindowsDllBlocklist.h rename to mozglue/dllservices/WindowsDllBlocklist.h diff --git a/mozglue/build/WindowsDllBlocklistCommon.h b/mozglue/dllservices/WindowsDllBlocklistCommon.h similarity index 100% rename from mozglue/build/WindowsDllBlocklistCommon.h rename to mozglue/dllservices/WindowsDllBlocklistCommon.h diff --git a/mozglue/build/WindowsDllBlocklistDefs.in b/mozglue/dllservices/WindowsDllBlocklistDefs.in similarity index 100% rename from mozglue/build/WindowsDllBlocklistDefs.in rename to mozglue/dllservices/WindowsDllBlocklistDefs.in diff --git a/mozglue/build/WindowsDllServices.h b/mozglue/dllservices/WindowsDllServices.h similarity index 61% rename from mozglue/build/WindowsDllServices.h rename to mozglue/dllservices/WindowsDllServices.h index f09b6a00e734..e11efb5f0504 100644 --- a/mozglue/build/WindowsDllServices.h +++ b/mozglue/dllservices/WindowsDllServices.h @@ -9,6 +9,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Authenticode.h" +#include "mozilla/LoaderAPIInterfaces.h" #include "mozilla/mozalloc.h" #include "mozilla/UniquePtr.h" #include "mozilla/Vector.h" @@ -21,6 +22,7 @@ # include "nsISupportsImpl.h" # include "nsString.h" # include "nsThreadUtils.h" +# include "prthread.h" #endif // defined(MOZILLA_INTERNAL_API) @@ -29,44 +31,6 @@ namespace mozilla { namespace glue { - -// Holds data about a top-level DLL load event, for the purposes of later -// evaluating the DLLs for trustworthiness. DLLs are loaded recursively, -// so we hold the top-level DLL and all child DLLs in mModules. -class ModuleLoadEvent { - public: - class ModuleInfo { - public: - ModuleInfo() = default; - ~ModuleInfo() = default; - ModuleInfo(const ModuleInfo& aOther) = delete; - ModuleInfo(ModuleInfo&& aOther) = default; - - ModuleInfo operator=(const ModuleInfo& aOther) = delete; - ModuleInfo operator=(ModuleInfo&& aOther) = delete; - - uintptr_t mBase; - UniquePtr mLdrName; - UniquePtr mFullPath; - double mLoadDurationMS; - }; - - ModuleLoadEvent() = default; - ~ModuleLoadEvent() = default; - ModuleLoadEvent(const ModuleLoadEvent& aOther) = delete; - ModuleLoadEvent(ModuleLoadEvent&& aOther) = default; - - ModuleLoadEvent& operator=(const ModuleLoadEvent& aOther) = delete; - ModuleLoadEvent& operator=(ModuleLoadEvent&& aOther) = delete; - - DWORD mThreadID; - uint64_t mProcessUptimeMS; - Vector mModules; - - // Stores instruction pointers, top-to-bottom. - Vector mStack; -}; - namespace detail { class DllServicesBase : public Authenticode { @@ -77,24 +41,23 @@ class DllServicesBase : public Authenticode { * this function should be used for is dispatching the event to our * event loop so that it may be handled in a safe context. */ - virtual void DispatchDllLoadNotification(PCUNICODE_STRING aDllName) = 0; + virtual void DispatchDllLoadNotification(ModuleLoadInfo&& aModLoadInfo) = 0; /** * This function accepts module load events to be processed later for * the untrusted modules telemetry ping. * * WARNING: This method is run from within the Windows loader and should - * only perform trivial, loader-friendly, operations. + * only perform trivial, loader-friendly operations. */ - virtual void NotifyUntrustedModuleLoads( - const Vector& - aEvents) = 0; + virtual void DispatchModuleLoadBacklogNotification( + ModuleLoadInfoVec&& aEvents) = 0; void SetAuthenticodeImpl(Authenticode* aAuthenticode) { mAuthenticode = aAuthenticode; } - // In debug builds, we override GetBinaryOrgName to add a Gecko-specific + // In debug builds we override GetBinaryOrgName to add a Gecko-specific // assertion. OTOH, we normally do not want people overriding this function, // so we'll make it final in the release case, thus covering all bases. #if defined(DEBUG) @@ -133,15 +96,49 @@ class DllServicesBase : public Authenticode { #if defined(MOZILLA_INTERNAL_API) +struct EnhancedModuleLoadInfo final { + explicit EnhancedModuleLoadInfo(ModuleLoadInfo&& aModLoadInfo) + : mNtLoadInfo(std::move(aModLoadInfo)) { + // Only populate mThreadName when we're on the same thread as the event + if (mNtLoadInfo.mThreadId == ::GetCurrentThreadId()) { + mThreadName = PR_GetThreadName(PR_GetCurrentThread()); + } + MOZ_ASSERT(!mNtLoadInfo.mSectionName.IsEmpty()); + } + + EnhancedModuleLoadInfo(EnhancedModuleLoadInfo&&) = default; + EnhancedModuleLoadInfo& operator=(EnhancedModuleLoadInfo&&) = default; + + EnhancedModuleLoadInfo(const EnhancedModuleLoadInfo&) = delete; + EnhancedModuleLoadInfo& operator=(const EnhancedModuleLoadInfo&) = delete; + + nsDependentString GetSectionName() const { + return mNtLoadInfo.mSectionName.AsString(); + } + + using BacktraceType = decltype(ModuleLoadInfo::mBacktrace); + + ModuleLoadInfo mNtLoadInfo; + nsCString mThreadName; +}; + class DllServices : public detail::DllServicesBase { public: - void DispatchDllLoadNotification(PCUNICODE_STRING aDllName) final { - nsDependentSubstring strDllName(aDllName->Buffer, - aDllName->Length / sizeof(wchar_t)); + void DispatchDllLoadNotification(ModuleLoadInfo&& aModLoadInfo) final { + nsCOMPtr runnable( + NewRunnableMethod>( + "DllServices::NotifyDllLoad", this, &DllServices::NotifyDllLoad, + std::move(aModLoadInfo))); - nsCOMPtr runnable(NewRunnableMethod( - "DllServices::NotifyDllLoad", this, &DllServices::NotifyDllLoad, - NS_IsMainThread(), strDllName)); + SystemGroup::Dispatch(TaskCategory::Other, runnable.forget()); + } + + void DispatchModuleLoadBacklogNotification( + ModuleLoadInfoVec&& aEvents) final { + nsCOMPtr runnable( + NewRunnableMethod>( + "DllServices::NotifyModuleLoadBacklog", this, + &DllServices::NotifyModuleLoadBacklog, std::move(aEvents))); SystemGroup::Dispatch(TaskCategory::Other, runnable.forget()); } @@ -161,8 +158,8 @@ class DllServices : public detail::DllServicesBase { DllServices() = default; ~DllServices() = default; - virtual void NotifyDllLoad(const bool aIsMainThread, - const nsString& aDllName) = 0; + virtual void NotifyDllLoad(EnhancedModuleLoadInfo&& aModLoadInfo) = 0; + virtual void NotifyModuleLoadBacklog(ModuleLoadInfoVec&& aEvents) = 0; }; #else @@ -174,12 +171,11 @@ class BasicDllServices final : public detail::DllServicesBase { ~BasicDllServices() = default; // Not useful in this class, so provide a default implementation - virtual void DispatchDllLoadNotification(PCUNICODE_STRING aDllName) override { - } + virtual void DispatchDllLoadNotification( + ModuleLoadInfo&& aModLoadInfo) override {} - virtual void NotifyUntrustedModuleLoads( - const Vector& aEvents) - override {} + virtual void DispatchModuleLoadBacklogNotification( + ModuleLoadInfoVec&& aEvents) override {} }; #endif // defined(MOZILLA_INTERNAL_API) diff --git a/mozglue/dllservices/WindowsFallbackLoaderAPI.cpp b/mozglue/dllservices/WindowsFallbackLoaderAPI.cpp new file mode 100644 index 000000000000..09cccee0615e --- /dev/null +++ b/mozglue/dllservices/WindowsFallbackLoaderAPI.cpp @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "WindowsFallbackLoaderAPI.h" + +namespace mozilla { + +ModuleLoadInfo FallbackLoaderAPI::ConstructAndNotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) { + ModuleLoadInfo loadInfo(aRequestedDllName); + + MOZ_ASSERT(mLoaderObserver); + if (mLoaderObserver) { + mLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName); + } + + return loadInfo; +} + +bool FallbackLoaderAPI::SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) { + MOZ_ASSERT(mLoaderObserver); + if (!mLoaderObserver) { + return false; + } + + return mLoaderObserver->SubstituteForLSP(aLSPLeafName, aOutHandle); +} + +void FallbackLoaderAPI::NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) { + aModuleLoadInfo.SetEndLoadTimeStamp(); + + if (NT_SUCCESS(aLoadNtStatus)) { + aModuleLoadInfo.CaptureBacktrace(); + } + + MOZ_ASSERT(mLoaderObserver); + if (mLoaderObserver) { + mLoaderObserver->OnEndDllLoad(aContext, aLoadNtStatus, + std::move(aModuleLoadInfo)); + } +} + +nt::AllocatedUnicodeString FallbackLoaderAPI::GetSectionName( + void* aSectionAddr) { + static const StaticDynamicallyLinkedFunctionPtr + pNtQueryVirtualMemory(L"ntdll.dll", "NtQueryVirtualMemory"); + MOZ_ASSERT(pNtQueryVirtualMemory); + + if (!pNtQueryVirtualMemory) { + return nt::AllocatedUnicodeString(); + } + + nt::MemorySectionNameBuf buf; + NTSTATUS ntStatus = + pNtQueryVirtualMemory(::GetCurrentProcess(), aSectionAddr, + MemorySectionName, &buf, sizeof(buf), nullptr); + if (!NT_SUCCESS(ntStatus)) { + return nt::AllocatedUnicodeString(); + } + + return nt::AllocatedUnicodeString(&buf.mSectionFileName); +} + +void FallbackLoaderAPI::SetObserver(nt::LoaderObserver* aLoaderObserver) { + mLoaderObserver = aLoaderObserver; +} + +} // namespace mozilla diff --git a/mozglue/dllservices/WindowsFallbackLoaderAPI.h b/mozglue/dllservices/WindowsFallbackLoaderAPI.h new file mode 100644 index 000000000000..3232df71f376 --- /dev/null +++ b/mozglue/dllservices/WindowsFallbackLoaderAPI.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_WindowsFallbackLoaderAPI_h +#define mozilla_WindowsFallbackLoaderAPI_h + +#include "mozilla/Attributes.h" +#include "NtLoaderAPI.h" + +namespace mozilla { + +class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS FallbackLoaderAPI final + : public nt::LoaderAPI { + public: + constexpr FallbackLoaderAPI() : mLoaderObserver(nullptr) {} + + ModuleLoadInfo ConstructAndNotifyBeginDllLoad( + void** aContext, PCUNICODE_STRING aRequestedDllName) final; + bool SubstituteForLSP(PCUNICODE_STRING aLSPLeafName, + PHANDLE aOutHandle) final; + void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus, + ModuleLoadInfo&& aModuleLoadInfo) final; + nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final; + + void SetObserver(nt::LoaderObserver* aLoaderObserver); + + private: + nt::LoaderObserver* mLoaderObserver; +}; + +} // namespace mozilla + +#endif // mozilla_WindowsFallbackLoaderAPI_h diff --git a/mozglue/build/gen_dll_blocklist_defs.py b/mozglue/dllservices/gen_dll_blocklist_defs.py similarity index 100% rename from mozglue/build/gen_dll_blocklist_defs.py rename to mozglue/dllservices/gen_dll_blocklist_defs.py diff --git a/mozglue/dllservices/moz.build b/mozglue/dllservices/moz.build new file mode 100644 index 000000000000..ba73d8c7ef1c --- /dev/null +++ b/mozglue/dllservices/moz.build @@ -0,0 +1,58 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if CONFIG['MOZ_WIDGET_TOOLKIT']: + + SOURCES += [ + # This file contains a |using namespace mozilla;| statement + 'WindowsDllBlocklist.cpp', + ] + + UNIFIED_SOURCES += [ + 'Authenticode.cpp', + 'LoaderObserver.cpp', + 'ModuleLoadFrame.cpp', + 'WindowsFallbackLoaderAPI.cpp', + ] + +OS_LIBS += [ + 'crypt32', + 'ntdll', + 'version', + 'wintrust', +] + +DELAYLOAD_DLLS += [ + 'crypt32.dll', + 'wintrust.dll', +] + +EXPORTS.mozilla += [ + 'Authenticode.h', + 'LoaderAPIInterfaces.h', + 'ModuleLoadInfo.h', + 'WindowsDllBlocklist.h', + 'WindowsDllBlocklistCommon.h', +] + +EXPORTS.mozilla.glue += [ + 'WindowsDllServices.h', +] + +# Generate DLL Blocklists +blocklist_header_types = ['A11y', 'Launcher', 'Legacy', 'Test'] +blocklist_file_leaf_tpl = 'WindowsDllBlocklist{0}Defs.h' +blocklist_files = tuple([blocklist_file_leaf_tpl.format(type) + for type in blocklist_header_types]) +GENERATED_FILES += [ + blocklist_files +] +blocklist_defs = GENERATED_FILES[blocklist_files] +blocklist_defs.script = 'gen_dll_blocklist_defs.py:gen_blocklists' +blocklist_defs.inputs = ['WindowsDllBlocklistDefs.in'] +EXPORTS.mozilla += ['!' + hdr for hdr in blocklist_files] + +FINAL_LIBRARY = 'mozglue' diff --git a/mozglue/build/MozglueUtils.h b/mozglue/misc/WinUtils.h similarity index 100% rename from mozglue/build/MozglueUtils.h rename to mozglue/misc/WinUtils.h diff --git a/mozglue/misc/WindowsUnicode.cpp b/mozglue/misc/WindowsUnicode.cpp new file mode 100644 index 000000000000..464380b6dad7 --- /dev/null +++ b/mozglue/misc/WindowsUnicode.cpp @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "WindowsUnicode.h" + +#include +// For UNICODE_STRING +#include + +#include + +namespace mozilla { +namespace glue { + +mozilla::UniquePtr WideToUTF8(const wchar_t* aStr, + const size_t aStrLenExclNul) { + int numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, aStrLenExclNul, nullptr, + 0, nullptr, nullptr); + if (!numConv) { + return nullptr; + } + + // Include room for the null terminator by adding one + auto buf = mozilla::MakeUnique(numConv + 1); + + numConv = ::WideCharToMultiByte(CP_UTF8, 0, aStr, aStrLenExclNul, buf.get(), + numConv, nullptr, nullptr); + if (!numConv) { + return nullptr; + } + + // Add null termination. numConv does not include the terminator, so we don't + // subtract 1 when indexing into buf. + buf[numConv] = 0; + + return buf; +} + +mozilla::UniquePtr WideToUTF8(const wchar_t* aStr) { + return WideToUTF8(aStr, wcslen(aStr)); +} + +mozilla::UniquePtr WideToUTF8(const std::wstring& aStr) { + return WideToUTF8(aStr.data(), aStr.length()); +} + +mozilla::UniquePtr WideToUTF8(PCUNICODE_STRING aStr) { + if (!aStr) { + return nullptr; + } + + return WideToUTF8(aStr->Buffer, aStr->Length / sizeof(WCHAR)); +} + +} // namespace glue +} // namespace mozilla diff --git a/mozglue/misc/WindowsUnicode.h b/mozglue/misc/WindowsUnicode.h new file mode 100644 index 000000000000..77fc376b9208 --- /dev/null +++ b/mozglue/misc/WindowsUnicode.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_glue_WindowsUnicode_h +#define mozilla_glue_WindowsUnicode_h + +#include "mozilla/UniquePtr.h" + +#include + +struct _UNICODE_STRING; + +namespace mozilla { +namespace glue { + +mozilla::UniquePtr WideToUTF8(const wchar_t* aStr, + const size_t aStrLenExclNul); + +mozilla::UniquePtr WideToUTF8(const wchar_t* aStr); +mozilla::UniquePtr WideToUTF8(const std::wstring& aStr); +mozilla::UniquePtr WideToUTF8(const _UNICODE_STRING* aStr); + +#if defined(bstr_t) +inline mozilla::UniquePtr WideToUTF8(const _bstr_t& aStr) { + return WideToUTF8(static_cast(aStr), aStr.length()); +} +#endif // defined(bstr_t) + +} // namespace glue +} // namespace mozilla + +#endif // mozilla_glue_WindowsUnicode_h diff --git a/mozglue/misc/moz.build b/mozglue/misc/moz.build index de7cd56bb916..15f7d96731a3 100644 --- a/mozglue/misc/moz.build +++ b/mozglue/misc/moz.build @@ -18,6 +18,7 @@ EXPORTS.mozilla += [ EXPORTS.mozilla.glue += [ 'Debug.h', + 'WinUtils.h', ] if CONFIG['OS_ARCH'] == 'WINNT': @@ -49,10 +50,14 @@ if CONFIG['OS_ARCH'] == 'WINNT': 'WindowsMapRemoteView.h', 'WindowsProcessMitigations.h', ] + EXPORTS.mozilla.glue += [ + 'WindowsUnicode.h', + ] SOURCES += [ 'TimeStamp_windows.cpp', 'WindowsMapRemoteView.cpp', 'WindowsProcessMitigations.cpp', + 'WindowsUnicode.cpp', ] OS_LIBS += ['dbghelp'] elif CONFIG['HAVE_CLOCK_MONOTONIC']: diff --git a/mozglue/moz.build b/mozglue/moz.build index a8c47266072c..abf2d169c240 100644 --- a/mozglue/moz.build +++ b/mozglue/moz.build @@ -13,6 +13,9 @@ if CONFIG['MOZ_LINKER']: if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': DIRS += ['android'] +if CONFIG['OS_TARGET'] == 'WINNT': + DIRS += ['dllservices'] + DIRS += [ 'baseprofiler', 'build', diff --git a/toolkit/crashreporter/nsExceptionHandler.cpp b/toolkit/crashreporter/nsExceptionHandler.cpp index 1d53bc693c22..8fcc88e3f1b0 100644 --- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -46,6 +46,7 @@ # include "nsDirectoryServiceUtils.h" # include "nsWindowsDllInterceptor.h" +# include "mozilla/WindowsDllBlocklist.h" # include "mozilla/WindowsVersion.h" # include "psapi.h" // For PERFORMANCE_INFORAMTION #elif defined(XP_MACOSX) @@ -96,7 +97,6 @@ using mozilla::InjectCrashRunnable; #include "mozilla/IOInterposer.h" #include "mozilla/mozalloc_oom.h" -#include "mozilla/WindowsDllBlocklist.h" #include "mozilla/recordreplay/ParentIPC.h" #if defined(XP_MACOSX) diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index 831037f074b2..91778117a311 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -82,8 +82,8 @@ #include "mozilla/ipc/XPCShellEnvironment.h" #if defined(XP_WIN) # include "mozilla/WindowsConsole.h" +# include "mozilla/WindowsDllBlocklist.h" #endif -#include "mozilla/WindowsDllBlocklist.h" #include "GMPProcessChild.h" #include "mozilla/gfx/GPUProcessImpl.h"