Bug 1542830: Part 4 - Modify mozglue to use new untrusted modules interfaces; r=mhowell

* At this point our DLL blocking infra is complicated enough that I decided to
  bite the bullet and move all of this code out of `mozglue/build` and into its
  own subdirectory, `mozglue/dllservices`.
* We delete the original `UntrustedDllsHandler` code which is now obsolete.
* We implement mozglue's `LoaderObserver`:
** When this observer registers itself with the launcher process API, it
   receives a vector containing all saved records of loaded DLLs that happened
   until that moment.
** This code handles profiler labels and stackwalking suppression.
** Once a load has completed, we either pass the load on to XUL for further
   processing, or save it for later if XUL is not initialized yet.
* mozglue has its own `ModuleLoadFrame` implementation for the legacy blocklist.
* `DllServicesBase` is updated to support the new interfaces.
* We implement `FallbackLoaderAPI` for `plugin-container`, `xpcshell`, and
  any other non-`firefox` processes that do not have a launcher process
  providing a loader API.
* We add some wide to UTF8 conversion functions.

Depends on D43157

Differential Revision: https://phabricator.services.mozilla.com/D43158

--HG--
rename : mozglue/build/Authenticode.cpp => mozglue/dllservices/Authenticode.cpp
rename : mozglue/build/Authenticode.h => mozglue/dllservices/Authenticode.h
rename : browser/app/winlauncher/freestanding/LoaderAPIInterfaces.h => mozglue/dllservices/LoaderAPIInterfaces.h
rename : browser/app/winlauncher/freestanding/ModuleLoadInfo.h => mozglue/dllservices/ModuleLoadInfo.h
rename : browser/app/winlauncher/NtLoaderAPI.h => mozglue/dllservices/NtLoaderAPI.h
rename : mozglue/build/WindowsDllBlocklist.cpp => mozglue/dllservices/WindowsDllBlocklist.cpp
rename : mozglue/build/WindowsDllBlocklist.h => mozglue/dllservices/WindowsDllBlocklist.h
rename : mozglue/build/WindowsDllBlocklistCommon.h => mozglue/dllservices/WindowsDllBlocklistCommon.h
rename : mozglue/build/WindowsDllBlocklistDefs.in => mozglue/dllservices/WindowsDllBlocklistDefs.in
rename : mozglue/build/WindowsDllServices.h => mozglue/dllservices/WindowsDllServices.h
rename : mozglue/build/gen_dll_blocklist_defs.py => mozglue/dllservices/gen_dll_blocklist_defs.py
rename : mozglue/build/moz.build => mozglue/dllservices/moz.build
rename : mozglue/build/MozglueUtils.h => mozglue/misc/WinUtils.h
extra : moz-landing-system : lando
This commit is contained in:
Aaron Klotz 2019-09-23 20:18:41 +00:00
parent f9be0e1c7f
commit fccd6eb9ba
36 changed files with 707 additions and 705 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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',

View file

@ -8,10 +8,6 @@ Library('winlauncher')
FORCE_STATIC_LIB = True
EXPORTS.mozilla += [
'NtLoaderAPI.h',
]
UNIFIED_SOURCES += [
'/ipc/mscom/ProcessRuntime.cpp',
'/widget/windows/WindowsConsole.cpp',

View file

@ -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;

View file

@ -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

View file

@ -10,10 +10,6 @@ EXPORTS.mozilla.mscom += [
'ProcessRuntimeShared.h',
]
LOCAL_INCLUDES += [
'/mozglue/build',
]
UNIFIED_SOURCES += [
'ProcessRuntimeShared.cpp',
]

View file

@ -8,7 +8,6 @@
#include <stdio.h>
#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 <windows.h>
# include <shlobj.h>

View file

@ -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 <windows.h>
#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<wchar_t[]> CopyString(const UniquePtr<wchar_t[]>& aOther) {
if (!aOther) {
return nullptr;
}
size_t chars = wcslen(aOther.get());
auto ret = MakeUnique<wchar_t[]>(chars + 1);
if (wcsncpy_s(ret.get(), chars + 1, aOther.get(), chars)) {
return nullptr;
}
return ret;
}
// Creates a UniquePtr<wchar_t[]> from a PCUNICODE_STRING string.
// Upon error, returns nullptr.
static UniquePtr<wchar_t[]> CopyString(PCUNICODE_STRING aOther) {
if (!aOther || !aOther->Buffer) {
return nullptr;
}
size_t chars = aOther->Length / sizeof(wchar_t);
auto ret = MakeUnique<wchar_t[]>(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<wchar_t[]> GetModuleFullPath(uintptr_t aModuleBase) {
size_t allocated = MAX_PATH;
auto ret = MakeUnique<wchar_t[]>(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<wchar_t[]>(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<ModuleLoadEvent::ModuleInfo, 0, InfallibleAllocPolicy> 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<int32_t> 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<bool> sInstanceHasBeenSet;
// Singleton reference
static StaticRefPtr<UntrustedDllsHandlerImpl> sInstance;
// Holds a list of module load events. This gets emptied upon calling
// UntrustedDllsHandler::TakePendingEvents
Vector<ModuleLoadEvent, 0, InfallibleAllocPolicy> mModuleLoadEvents;
// Holds a list of module full paths that we've already handled, so we can
// skip duplicates.
Vector<mozilla::UniquePtr<wchar_t[]>> 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<UntrustedDllsHandlerImpl> 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<UntrustedDllsHandlerImpl> 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<void*[]>(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<ModuleLoadEvent, 0, InfallibleAllocPolicy>& aOut) {
// Hold a reference to ensure we don't get deleted during this call.
RefPtr<UntrustedDllsHandlerImpl> refHolder(this);
if (!refHolder) {
return false;
}
AutoCriticalSection lock(&mDataLock);
mModuleLoadEvents.swap(aOut);
return !aOut.empty();
}
};
Atomic<bool> UntrustedDllsHandlerImpl::sInstanceHasBeenSet;
StaticRefPtr<UntrustedDllsHandlerImpl> 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<UntrustedDllsHandlerImpl> 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<ModuleLoadEvent, 0, InfallibleAllocPolicy>& aOut) {
RefPtr<UntrustedDllsHandlerImpl> p(UntrustedDllsHandlerImpl::GetInstance());
if (!p) {
return false;
}
return p->TakePendingEvents(aOut);
}
} // namespace glue
} // namespace mozilla

View file

@ -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 <windows.h>
#include <winternl.h> // 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<ModuleLoadEvent, 0, InfallibleAllocPolicy>& aOut);
};
} // namespace glue
} // namespace mozilla
#endif // mozilla_glue_UntrustedDllsHandler_h

View file

@ -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'):

View file

@ -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

View file

@ -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<char[]>&& aDynamicStringStorage)
: mProfilerLabel(std::move(aLabel)),
mDynamicStringStorage(std::move(aDynamicStringStorage)) {}
mozilla::ProfilerLabel mProfilerLabel;
mozilla::UniquePtr<char[]> 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<char[]> 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> loadContext(static_cast<LoadContext*>(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<ModuleLoadInfo>(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

View file

@ -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

View file

@ -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 <string.h>
#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<GetNtLoaderAPIFn>(
::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<WCHAR[]> nameBuf;
const WCHAR* name = nullptr;
if (IsNullTerminated(aRequestedDllName)) {
name = aRequestedDllName->Buffer;
} else {
USHORT charLenExclNul = aRequestedDllName->Length / sizeof(WCHAR);
USHORT charLenInclNul = charLenExclNul + 1;
nameBuf = MakeUnique<WCHAR[]>(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<HMODULE>(aHandle));
mLoadInfo.mBaseAddr = baseAddr;
if (!mAlreadyLoaded) {
mLoadInfo.mSectionName = sLoaderAPI->GetSectionName(baseAddr);
}
}
} // namespace glue
} // namespace mozilla

View file

@ -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

View file

@ -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<BaseThreadInitThunk_func>
@ -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<glue::ModuleLoadEvent, 0, InfallibleAllocPolicy> 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<void*, 4>* 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<void*, 4>;
#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<decltype(&::LdrRegisterDllNotification)>(
::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"),
"LdrRegisterDllNotification"));
MOZ_DIAGNOSTIC_ASSERT(pLdrRegisterDllNotification);
mozilla::DebugOnly<NTSTATUS> ntStatus = pLdrRegisterDllNotification(
0, &DllLoadNotification, nullptr, &gNotificationCookie);
MOZ_ASSERT(NT_SUCCESS(ntStatus));
}
gMozglueLoaderObserver.Forward(aSvc);
}
gDllServices = aSvc;
if (IsUntrustedDllsHandlerEnabled() && gDllServices) {
Vector<glue::ModuleLoadEvent, 0, InfallibleAllocPolicy> 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();
}

View file

@ -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<wchar_t[]> mLdrName;
UniquePtr<wchar_t[]> 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<ModuleInfo, 0, InfallibleAllocPolicy> mModules;
// Stores instruction pointers, top-to-bottom.
Vector<uintptr_t, 0, InfallibleAllocPolicy> 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<glue::ModuleLoadEvent, 0, InfallibleAllocPolicy>&
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<nsIRunnable> runnable(
NewRunnableMethod<StoreCopyPassByRRef<EnhancedModuleLoadInfo>>(
"DllServices::NotifyDllLoad", this, &DllServices::NotifyDllLoad,
std::move(aModLoadInfo)));
nsCOMPtr<nsIRunnable> runnable(NewRunnableMethod<bool, nsString>(
"DllServices::NotifyDllLoad", this, &DllServices::NotifyDllLoad,
NS_IsMainThread(), strDllName));
SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
}
void DispatchModuleLoadBacklogNotification(
ModuleLoadInfoVec&& aEvents) final {
nsCOMPtr<nsIRunnable> runnable(
NewRunnableMethod<StoreCopyPassByRRef<ModuleLoadInfoVec>>(
"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<glue::ModuleLoadEvent, 0, InfallibleAllocPolicy>& aEvents)
override {}
virtual void DispatchModuleLoadBacklogNotification(
ModuleLoadInfoVec&& aEvents) override {}
};
#endif // defined(MOZILLA_INTERNAL_API)

View file

@ -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<decltype(
&::NtQueryVirtualMemory)>
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

View file

@ -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

View file

@ -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'

View file

@ -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 <windows.h>
// For UNICODE_STRING
#include <winternl.h>
#include <string.h>
namespace mozilla {
namespace glue {
mozilla::UniquePtr<char[]> 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<char[]>(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<char[]> WideToUTF8(const wchar_t* aStr) {
return WideToUTF8(aStr, wcslen(aStr));
}
mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr) {
return WideToUTF8(aStr.data(), aStr.length());
}
mozilla::UniquePtr<char[]> WideToUTF8(PCUNICODE_STRING aStr) {
if (!aStr) {
return nullptr;
}
return WideToUTF8(aStr->Buffer, aStr->Length / sizeof(WCHAR));
}
} // namespace glue
} // namespace mozilla

View file

@ -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 <string>
struct _UNICODE_STRING;
namespace mozilla {
namespace glue {
mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr,
const size_t aStrLenExclNul);
mozilla::UniquePtr<char[]> WideToUTF8(const wchar_t* aStr);
mozilla::UniquePtr<char[]> WideToUTF8(const std::wstring& aStr);
mozilla::UniquePtr<char[]> WideToUTF8(const _UNICODE_STRING* aStr);
#if defined(bstr_t)
inline mozilla::UniquePtr<char[]> WideToUTF8(const _bstr_t& aStr) {
return WideToUTF8(static_cast<const wchar_t*>(aStr), aStr.length());
}
#endif // defined(bstr_t)
} // namespace glue
} // namespace mozilla
#endif // mozilla_glue_WindowsUnicode_h

View file

@ -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']:

View file

@ -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',

View file

@ -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)

View file

@ -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"