Bug 1587642 - Make the blocklist work when the process heap is not initialized. r=aklotz
authorToshihito Kikuchi <tkikuchi@mozilla.com>
Fri, 15 Nov 2019 22:53:49 +0000
changeset 502308 edafd8d2b8c67977bd4658891c1c7063ff9c5b83
parent 502307 9cb264733132c914ab9df9e06c226f967abe8e00
child 502309 5258157edfeea095a4d96b65f819e2b6143af6c0
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaklotz
bugs1587642
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
browser/app/winlauncher/freestanding/DllBlocklist.cpp
browser/app/winlauncher/freestanding/Freestanding.h
browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
browser/app/winlauncher/freestanding/LoaderPrivateAPI.h
mozglue/misc/NativeNt.h
--- a/browser/app/winlauncher/freestanding/DllBlocklist.cpp
+++ b/browser/app/winlauncher/freestanding/DllBlocklist.cpp
@@ -85,17 +85,19 @@ void NativeNtBlockSet::Add(const UNICODE
     if (::RtlEqualUnicodeString(&entry->mName, &aName, TRUE) &&
         aVersion == entry->mVersion) {
       return;
     }
   }
 
   // Not present, add it
   NativeNtBlockSetEntry* newEntry = NewEntry(aName, aVersion, mFirstEntry);
-  mFirstEntry = newEntry;
+  if (newEntry) {
+    mFirstEntry = newEntry;
+  }
 }
 
 void NativeNtBlockSet::Write(HANDLE aFile) {
   // NB: If this function is called, it is long after kernel32 is initialized,
   // so it is safe to use Win32 calls here.
   DWORD nBytes;
   char buf[MAX_PATH];
 
@@ -306,40 +308,49 @@ NTSTATUS NTAPI patched_NtMapViewOfSectio
   }
 
   // We don't care about mappings that aren't MEM_IMAGE
   if (!(mbi.Type & MEM_IMAGE)) {
     return stubStatus;
   }
 
   // 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);
   return STATUS_ACCESS_DENIED;
 }
 
 }  // namespace freestanding
 }  // namespace mozilla
--- a/browser/app/winlauncher/freestanding/Freestanding.h
+++ b/browser/app/winlauncher/freestanding/Freestanding.h
@@ -24,17 +24,24 @@ namespace freestanding {
 /**
  * Since this library is the only part of firefox.exe that needs special
  * treatment with respect to the heap, we implement |RtlNew| and |RtlDelete|
  * to be used instead of |new| and |delete| for any heap allocations inside
  * the freestanding library.
  */
 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;
   }
 
   return new (ptr) T(std::forward<Args>(aArgs)...);
 }
 
 template <typename T>
--- a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
+++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.cpp
@@ -79,16 +79,17 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONS
   // LoaderAPI
   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;
+  nt::MemorySectionNameBuf GetSectionNameBuffer(void* aSectionAddr) final;
 
   // LoaderPrivateAPI
   void NotifyBeginDllLoad(void** aContext,
                           PCUNICODE_STRING aRequestedDllName) final;
   void NotifyBeginDllLoad(ModuleLoadInfo& aModuleLoadInfo, void** aContext,
                           PCUNICODE_STRING aRequestedDllName) final;
   void SetObserver(nt::LoaderObserver* aNewObserver) final;
   bool IsDefaultObserver() const final;
@@ -202,16 +203,31 @@ nt::AllocatedUnicodeString LoaderPrivate
                              &buf, sizeof(buf), nullptr);
   if (!NT_SUCCESS(ntStatus)) {
     return nt::AllocatedUnicodeString();
   }
 
   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);
   gLoaderObserver->OnBeginDllLoad(aContext, aRequestedDllName);
 }
 
 void LoaderPrivateAPIImp::NotifyBeginDllLoad(
     ModuleLoadInfo& aModuleLoadInfo, void** aContext,
--- a/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h
+++ b/browser/app/winlauncher/freestanding/LoaderPrivateAPI.h
@@ -36,16 +36,22 @@ class NS_NO_VTABLE LoaderPrivateAPI : pu
    */
   virtual void SetObserver(nt::LoaderObserver* aNewObserver) = 0;
 
   /**
    * Returns true if the current nt::LoaderObserver is the launcher process's
    * 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;
 };
 
 /**
  * Ensures that any statics in the freestanding library are initialized.
  */
 void EnsureInitialized();
 
 extern LoaderPrivateAPI& gLoaderPrivateAPI;
--- a/mozglue/misc/NativeNt.h
+++ b/mozglue/misc/NativeNt.h
@@ -237,16 +237,22 @@ struct MemorySectionNameBuf : public _ME
   MemorySectionNameBuf() {
     mSectionFileName.Length = 0;
     mSectionFileName.MaximumLength = sizeof(mBuf);
     mSectionFileName.Buffer = mBuf;
   }
 
   // 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,
                                     uint16_t& aPos, uint16_t aStartIndex = 0) {
   const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
 
   for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
     if (aStr.Buffer[curIndex] == aChar) {