Bug 1587642 - Make the blocklist work when the process heap is not initialized. r=aklotz

`patched_NtMapViewOfSection` uses the process default heap to copy a string.
However, `patched_NtMapViewOfSection` can be invoked even before the process
heap is initialized. One example we found is Windows Defender's EAF, with
which "verifier.dll" is loaded before the process heap is initialized.

This patch adds a check whether the heap is initialized or not in
`patched_NtMapViewOfSection` and `NativeNtBlockSet::Add`. This also minimizes
the usage of the heap, i.e. not copying a string when we block a dll.

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Toshihito Kikuchi 2019-11-15 22:53:49 +00:00
parent a1a57a8bc5
commit bd457a84a8
5 changed files with 56 additions and 10 deletions

View File

@ -90,7 +90,9 @@ void NativeNtBlockSet::Add(const UNICODE_STRING& aName, uint64_t aVersion) {
// Not present, add it
NativeNtBlockSetEntry* newEntry = NewEntry(aName, aVersion, mFirstEntry);
mFirstEntry = newEntry;
if (newEntry) {
mFirstEntry = newEntry;
}
}
void NativeNtBlockSet::Write(HANDLE aFile) {
@ -311,30 +313,39 @@ NTSTATUS NTAPI patched_NtMapViewOfSection(
}
// Get the section name
nt::AllocatedUnicodeString sectionFileName(
gLoaderPrivateAPI.GetSectionName(*aBaseAddress));
nt::MemorySectionNameBuf sectionFileName(
gLoaderPrivateAPI.GetSectionNameBuffer(*aBaseAddress));
if (sectionFileName.IsEmpty()) {
::NtUnmapViewOfSection(aProcess, *aBaseAddress);
return STATUS_ACCESS_DENIED;
}
// Find the leaf name
UNICODE_STRING leaf;
nt::GetLeafName(&leaf, sectionFileName);
UNICODE_STRING leafOnStack;
nt::GetLeafName(&leafOnStack, sectionFileName);
// Check blocklist
BlockAction blockAction = IsDllAllowed(leaf, *aBaseAddress);
BlockAction blockAction = IsDllAllowed(leafOnStack, *aBaseAddress);
if (blockAction == BlockAction::Allow) {
ModuleLoadFrame::NotifySectionMap(std::move(sectionFileName), *aBaseAddress,
stubStatus);
if (nt::RtlGetProcessHeap()) {
ModuleLoadFrame::NotifySectionMap(
nt::AllocatedUnicodeString(sectionFileName), *aBaseAddress,
stubStatus);
}
return stubStatus;
}
if (blockAction == BlockAction::SubstituteLSP) {
// The process heap needs to be available here because
// NotifyLSPSubstitutionRequired below copies a given string into the heap.
// We use a soft assert here, assuming LSP load always occurs after the heap
// is initialized.
MOZ_ASSERT(nt::RtlGetProcessHeap());
// Notify patched_LdrLoadDll that it will be necessary to perform a
// substitution before returning.
ModuleLoadFrame::NotifyLSPSubstitutionRequired(&leaf);
ModuleLoadFrame::NotifyLSPSubstitutionRequired(&leafOnStack);
}
::NtUnmapViewOfSection(aProcess, *aBaseAddress);

View File

@ -29,7 +29,14 @@ namespace freestanding {
*/
template <typename T, typename... Args>
inline static T* RtlNew(Args&&... aArgs) {
void* ptr = ::RtlAllocateHeap(nt::RtlGetProcessHeap(), 0, sizeof(T));
HANDLE processHeap = nt::RtlGetProcessHeap();
if (!processHeap) {
// Handle the case where the process heap is not initialized because
// passing nullptr to RtlAllocateHeap crashes the process.
return nullptr;
}
void* ptr = ::RtlAllocateHeap(processHeap, 0, sizeof(T));
if (!ptr) {
return nullptr;
}

View File

@ -84,6 +84,7 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS LoaderPrivateAPIImp final
void NotifyEndDllLoad(void* aContext, NTSTATUS aLoadNtStatus,
ModuleLoadInfo&& aModuleLoadInfo) final;
nt::AllocatedUnicodeString GetSectionName(void* aSectionAddr) final;
nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final;
// LoaderPrivateAPI
void NotifyBeginDllLoad(void** aContext,
@ -207,6 +208,21 @@ nt::AllocatedUnicodeString LoaderPrivateAPIImp::GetSectionName(
return nt::AllocatedUnicodeString(&buf.mSectionFileName);
}
nt::MemorySectionNameBuf LoaderPrivateAPIImp::GetSectionNameBuffer(
void* aSectionAddr) {
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
nt::MemorySectionNameBuf buf;
NTSTATUS ntStatus =
::NtQueryVirtualMemory(kCurrentProcess, aSectionAddr, MemorySectionName,
&buf, sizeof(buf), nullptr);
if (!NT_SUCCESS(ntStatus)) {
return nt::MemorySectionNameBuf();
}
return buf;
}
void LoaderPrivateAPIImp::NotifyBeginDllLoad(
void** aContext, PCUNICODE_STRING aRequestedDllName) {
nt::AutoSharedLock lock(gLoaderObserverLock);

View File

@ -41,6 +41,12 @@ class NS_NO_VTABLE LoaderPrivateAPI : public nt::LoaderAPI {
* built-in observer.
*/
virtual bool IsDefaultObserver() const = 0;
/**
* Returns the name of a given mapped section address as a local instance of
* nt::MemorySectionNameBuf. This does not involve heap allocation.
*/
virtual nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) = 0;
};
/**

View File

@ -242,6 +242,12 @@ struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
// Native NT paths, so we can't assume MAX_PATH. Use a larger buffer.
WCHAR mBuf[2 * MAX_PATH];
bool IsEmpty() const {
return !mSectionFileName.Buffer || !mSectionFileName.Length;
}
operator PCUNICODE_STRING() const { return &mSectionFileName; }
};
inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar,