tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

NativeNt.h (54874B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_NativeNt_h
      8 #define mozilla_NativeNt_h
      9 
     10 #include <stdint.h>
     11 #include <windows.h>
     12 #include <winnt.h>
     13 #include <winternl.h>
     14 
     15 #include <algorithm>
     16 #include <utility>
     17 
     18 #include "mozilla/Attributes.h"
     19 #include "mozilla/CheckedArithmetic.h"
     20 #include "mozilla/Maybe.h"
     21 #include "mozilla/Range.h"
     22 #include "mozilla/Span.h"
     23 #include "mozilla/WinHeaderOnlyUtils.h"
     24 #include "mozilla/interceptor/MMPolicies.h"
     25 #include "mozilla/interceptor/TargetFunction.h"
     26 
     27 #if defined(MOZILLA_INTERNAL_API)
     28 #  include "nsString.h"
     29 #endif  // defined(MOZILLA_INTERNAL_API)
     30 
     31 // The declarations within this #if block are intended to be used for initial
     32 // process initialization ONLY. You probably don't want to be using these in
     33 // normal Gecko code!
     34 #if !defined(MOZILLA_INTERNAL_API)
     35 
     36 extern "C" {
     37 
     38 #  if !defined(STATUS_ACCESS_DENIED)
     39 #    define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
     40 #  endif  // !defined(STATUS_ACCESS_DENIED)
     41 
     42 #  if !defined(STATUS_DLL_NOT_FOUND)
     43 #    define STATUS_DLL_NOT_FOUND ((NTSTATUS)0xC0000135L)
     44 #  endif  // !defined(STATUS_DLL_NOT_FOUND)
     45 
     46 #  if !defined(STATUS_UNSUCCESSFUL)
     47 #    define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
     48 #  endif  // !defined(STATUS_UNSUCCESSFUL)
     49 
     50 #  if !defined(STATUS_INFO_LENGTH_MISMATCH)
     51 #    define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
     52 #  endif
     53 
     54 enum SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 };
     55 
     56 NTSTATUS NTAPI NtMapViewOfSection(
     57    HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits,
     58    SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
     59    SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
     60    ULONG aProtectionFlags);
     61 
     62 NTSTATUS NTAPI NtUnmapViewOfSection(HANDLE aProcess, PVOID aBaseAddress);
     63 
     64 enum MEMORY_INFORMATION_CLASS {
     65  MemoryBasicInformation = 0,
     66  MemorySectionName = 2
     67 };
     68 
     69 // NB: When allocating, space for the buffer must also be included
     70 typedef struct _MEMORY_SECTION_NAME {
     71  UNICODE_STRING mSectionFileName;
     72 } MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
     73 
     74 NTSTATUS NTAPI NtQueryVirtualMemory(HANDLE aProcess, PVOID aBaseAddress,
     75                                    MEMORY_INFORMATION_CLASS aMemInfoClass,
     76                                    PVOID aMemInfo, SIZE_T aMemInfoLen,
     77                                    PSIZE_T aReturnLen);
     78 
     79 LONG NTAPI RtlCompareUnicodeString(PCUNICODE_STRING aStr1,
     80                                   PCUNICODE_STRING aStr2,
     81                                   BOOLEAN aCaseInsensitive);
     82 
     83 BOOLEAN NTAPI RtlEqualUnicodeString(PCUNICODE_STRING aStr1,
     84                                    PCUNICODE_STRING aStr2,
     85                                    BOOLEAN aCaseInsensitive);
     86 
     87 NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW aOutVersionInformation);
     88 
     89 VOID NTAPI RtlAcquireSRWLockExclusive(PSRWLOCK aLock);
     90 VOID NTAPI RtlAcquireSRWLockShared(PSRWLOCK aLock);
     91 
     92 VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
     93 VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock);
     94 
     95 NTSTATUS NTAPI RtlSleepConditionVariableSRW(
     96    PCONDITION_VARIABLE aConditionVariable, PSRWLOCK aSRWLock,
     97    PLARGE_INTEGER aTimeOut, ULONG aFlags);
     98 VOID NTAPI RtlWakeAllConditionVariable(PCONDITION_VARIABLE aConditionVariable);
     99 
    100 ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
    101 VOID NTAPI RtlSetLastWin32Error(DWORD aError);
    102 DWORD NTAPI RtlGetLastWin32Error();
    103 
    104 VOID NTAPI RtlRunOnceInitialize(PRTL_RUN_ONCE aRunOnce);
    105 
    106 NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
    107                                   PVOID aBuffer, SIZE_T aNumBytesToRead,
    108                                   PSIZE_T aNumBytesRead);
    109 
    110 NTSTATUS NTAPI LdrLoadDll(PWCHAR aDllPath, PULONG aFlags,
    111                          PUNICODE_STRING aDllName, PHANDLE aOutHandle);
    112 
    113 typedef ULONG(NTAPI* PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID*);
    114 NTSTATUS NTAPI RtlRunOnceExecuteOnce(PRTL_RUN_ONCE aRunOnce,
    115                                     PRTL_RUN_ONCE_INIT_FN aInitFn,
    116                                     PVOID aContext, PVOID* aParameter);
    117 
    118 }  // extern "C"
    119 
    120 #endif  // !defined(MOZILLA_INTERNAL_API)
    121 
    122 extern "C" {
    123 PVOID NTAPI RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize);
    124 
    125 PVOID NTAPI RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem,
    126                              SIZE_T aNewSize);
    127 
    128 BOOLEAN NTAPI RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase);
    129 
    130 BOOLEAN NTAPI RtlQueryPerformanceCounter(LARGE_INTEGER* aPerfCount);
    131 
    132 #define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 1
    133 #define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING 2
    134 NTSTATUS NTAPI RtlDuplicateUnicodeString(ULONG aFlags, PCUNICODE_STRING aSrc,
    135                                         PUNICODE_STRING aDest);
    136 
    137 VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING aUnicodeString);
    138 }  // extern "C"
    139 
    140 namespace mozilla {
    141 namespace nt {
    142 
    143 /**
    144 * This class encapsulates a UNICODE_STRING that owns its own buffer. The
    145 * buffer is always NULL terminated, thus allowing us to cast to a wide C-string
    146 * without requiring any mutation.
    147 *
    148 * We only allow creation of this owned buffer from outside XUL.
    149 */
    150 class AllocatedUnicodeString final {
    151 public:
    152  AllocatedUnicodeString() : mUnicodeString() {}
    153 
    154 #if defined(MOZILLA_INTERNAL_API)
    155  AllocatedUnicodeString(const AllocatedUnicodeString& aOther) = delete;
    156 
    157  AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) =
    158      delete;
    159 #else
    160  explicit AllocatedUnicodeString(PCUNICODE_STRING aSrc) {
    161    if (!aSrc) {
    162      mUnicodeString = {};
    163      return;
    164    }
    165 
    166    Duplicate(aSrc);
    167  }
    168 
    169  explicit AllocatedUnicodeString(const char* aSrc) {
    170    if (!aSrc) {
    171      mUnicodeString = {};
    172      return;
    173    }
    174 
    175    Duplicate(aSrc);
    176  }
    177 
    178  AllocatedUnicodeString(const AllocatedUnicodeString& aOther) {
    179    Duplicate(&aOther.mUnicodeString);
    180  }
    181 
    182  AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) {
    183    Clear();
    184    Duplicate(&aOther.mUnicodeString);
    185    return *this;
    186  }
    187 
    188  AllocatedUnicodeString& operator=(PCUNICODE_STRING aSrc) {
    189    Clear();
    190    Duplicate(aSrc);
    191    return *this;
    192  }
    193 #endif  // defined(MOZILLA_INTERNAL_API)
    194 
    195  AllocatedUnicodeString(AllocatedUnicodeString&& aOther)
    196      : mUnicodeString(aOther.mUnicodeString) {
    197    aOther.mUnicodeString = {};
    198  }
    199 
    200  AllocatedUnicodeString& operator=(AllocatedUnicodeString&& aOther) {
    201    Clear();
    202    mUnicodeString = aOther.mUnicodeString;
    203    aOther.mUnicodeString = {};
    204    return *this;
    205  }
    206 
    207  ~AllocatedUnicodeString() { Clear(); }
    208 
    209  bool IsEmpty() const {
    210    return !mUnicodeString.Buffer || !mUnicodeString.Length;
    211  }
    212 
    213  operator PCUNICODE_STRING() const { return &mUnicodeString; }
    214 
    215  operator const WCHAR*() const { return mUnicodeString.Buffer; }
    216 
    217  USHORT CharLen() const { return mUnicodeString.Length / sizeof(WCHAR); }
    218 
    219 #if defined(MOZILLA_INTERNAL_API)
    220  nsDependentString AsString() const {
    221    if (!mUnicodeString.Buffer) {
    222      return nsDependentString();
    223    }
    224 
    225    // We can use nsDependentString here as we guaranteed null termination
    226    // when we allocated the string.
    227    return nsDependentString(mUnicodeString.Buffer, CharLen());
    228  }
    229 #endif  // defined(MOZILLA_INTERNAL_API)
    230 
    231 private:
    232 #if !defined(MOZILLA_INTERNAL_API)
    233  void Duplicate(PCUNICODE_STRING aSrc) {
    234    MOZ_ASSERT(aSrc);
    235 
    236    // We duplicate with null termination so that this string may be used
    237    // as a wide C-string without any further manipulation.
    238    NTSTATUS ntStatus = ::RtlDuplicateUnicodeString(
    239        RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, aSrc, &mUnicodeString);
    240    MOZ_ASSERT(NT_SUCCESS(ntStatus));
    241    if (!NT_SUCCESS(ntStatus)) {
    242      // Make sure that mUnicodeString does not contain bogus data
    243      // (since not all callers zero it out before invoking)
    244      mUnicodeString = {};
    245    }
    246  }
    247 
    248  void Duplicate(const char* aSrc) {
    249    MOZ_ASSERT(aSrc);
    250 
    251    ANSI_STRING ansiStr;
    252    RtlInitAnsiString(&ansiStr, aSrc);
    253    NTSTATUS ntStatus =
    254        ::RtlAnsiStringToUnicodeString(&mUnicodeString, &ansiStr, TRUE);
    255    MOZ_ASSERT(NT_SUCCESS(ntStatus));
    256    if (!NT_SUCCESS(ntStatus)) {
    257      mUnicodeString = {};
    258    }
    259  }
    260 #endif  // !defined(MOZILLA_INTERNAL_API)
    261 
    262  void Clear() {
    263    if (!mUnicodeString.Buffer) {
    264      return;
    265    }
    266 
    267    ::RtlFreeUnicodeString(&mUnicodeString);
    268    mUnicodeString = {};
    269  }
    270 
    271  UNICODE_STRING mUnicodeString;
    272 };
    273 
    274 #if !defined(MOZILLA_INTERNAL_API)
    275 
    276 struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
    277  MemorySectionNameBuf() {
    278    mSectionFileName.Length = 0;
    279    mSectionFileName.MaximumLength = sizeof(mBuf);
    280    mSectionFileName.Buffer = mBuf;
    281  }
    282 
    283  MemorySectionNameBuf(const MemorySectionNameBuf& aOther) { *this = aOther; }
    284 
    285  MemorySectionNameBuf(MemorySectionNameBuf&& aOther) {
    286    *this = std::move(aOther);
    287  }
    288 
    289  // We cannot use default copy here because mSectionFileName.Buffer needs to
    290  // be updated to point to |this->mBuf|, not |aOther.mBuf|.
    291  MemorySectionNameBuf& operator=(const MemorySectionNameBuf& aOther) {
    292    mSectionFileName.Length = aOther.mSectionFileName.Length;
    293    mSectionFileName.MaximumLength = sizeof(mBuf);
    294    MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
    295    mSectionFileName.Buffer = mBuf;
    296    memcpy(mBuf, aOther.mBuf, aOther.mSectionFileName.Length);
    297    return *this;
    298  }
    299 
    300  MemorySectionNameBuf& operator=(MemorySectionNameBuf&& aOther) {
    301    mSectionFileName.Length = aOther.mSectionFileName.Length;
    302    aOther.mSectionFileName.Length = 0;
    303    mSectionFileName.MaximumLength = sizeof(mBuf);
    304    MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
    305    aOther.mSectionFileName.MaximumLength = sizeof(aOther.mBuf);
    306    mSectionFileName.Buffer = mBuf;
    307    memmove(mBuf, aOther.mBuf, mSectionFileName.Length);
    308    return *this;
    309  }
    310 
    311  // Native NT paths, so we can't assume MAX_PATH. Use a larger buffer.
    312  WCHAR mBuf[2 * MAX_PATH];
    313 
    314  bool IsEmpty() const {
    315    return !mSectionFileName.Buffer || !mSectionFileName.Length;
    316  }
    317 
    318  operator PCUNICODE_STRING() const { return &mSectionFileName; }
    319 };
    320 
    321 class MemorySectionNameOnHeap {
    322  UniquePtr<uint8_t[]> mBuffer;
    323 
    324  MemorySectionNameOnHeap() = default;
    325  explicit MemorySectionNameOnHeap(size_t aBufferLen)
    326      : mBuffer(MakeUnique<uint8_t[]>(aBufferLen)) {}
    327 
    328 public:
    329  static MemorySectionNameOnHeap GetBackingFilePath(HANDLE aProcess,
    330                                                    void* aSectionAddr) {
    331    SIZE_T bufferLen = MAX_PATH * 2;
    332    do {
    333      MemorySectionNameOnHeap sectionName(bufferLen);
    334 
    335      SIZE_T requiredBytes;
    336      NTSTATUS ntStatus = ::NtQueryVirtualMemory(
    337          aProcess, aSectionAddr, MemorySectionName, sectionName.mBuffer.get(),
    338          bufferLen, &requiredBytes);
    339      if (NT_SUCCESS(ntStatus)) {
    340        return sectionName;
    341      }
    342 
    343      if (ntStatus != STATUS_INFO_LENGTH_MISMATCH ||
    344          bufferLen >= requiredBytes) {
    345        break;
    346      }
    347 
    348      bufferLen = requiredBytes;
    349    } while (1);
    350 
    351    return MemorySectionNameOnHeap();
    352  }
    353 
    354  // Allow move & Disallow copy
    355  MemorySectionNameOnHeap(MemorySectionNameOnHeap&&) = default;
    356  MemorySectionNameOnHeap& operator=(MemorySectionNameOnHeap&&) = default;
    357  MemorySectionNameOnHeap(const MemorySectionNameOnHeap&) = delete;
    358  MemorySectionNameOnHeap& operator=(const MemorySectionNameOnHeap&) = delete;
    359 
    360  PCUNICODE_STRING AsUnicodeString() const {
    361    return reinterpret_cast<PCUNICODE_STRING>(mBuffer.get());
    362  }
    363 };
    364 
    365 inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar,
    366                                    uint16_t& aPos, uint16_t aStartIndex = 0) {
    367  const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
    368 
    369  for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
    370    if (aStr.Buffer[curIndex] == aChar) {
    371      aPos = curIndex;
    372      return true;
    373    }
    374  }
    375 
    376  return false;
    377 }
    378 
    379 inline bool IsHexDigit(WCHAR aChar) {
    380  return (aChar >= L'0' && aChar <= L'9') || (aChar >= L'A' && aChar <= L'F') ||
    381         (aChar >= L'a' && aChar <= L'f');
    382 }
    383 
    384 inline bool MatchUnicodeString(const UNICODE_STRING& aStr,
    385                               bool (*aPredicate)(WCHAR)) {
    386  WCHAR* cur = aStr.Buffer;
    387  WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)];
    388  while (cur < end) {
    389    if (!aPredicate(*cur)) {
    390      return false;
    391    }
    392 
    393    ++cur;
    394  }
    395 
    396  return true;
    397 }
    398 
    399 inline bool Contains12DigitHexString(const UNICODE_STRING& aLeafName) {
    400  // Quick check: If the string is too short, don't bother
    401  // (We need at least 12 hex digits, one char for '.', and 3 for extension)
    402  const USHORT kMinLen = (12 + 1 + 3) * sizeof(wchar_t);
    403  if (aLeafName.Length < kMinLen) {
    404    return false;
    405  }
    406 
    407  uint16_t start, end;
    408  if (!FindCharInUnicodeString(aLeafName, L'.', start)) {
    409    return false;
    410  }
    411 
    412  ++start;
    413  if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) {
    414    return false;
    415  }
    416 
    417  if (end - start != 12) {
    418    return false;
    419  }
    420 
    421  UNICODE_STRING test;
    422  test.Buffer = &aLeafName.Buffer[start];
    423  test.Length = (end - start) * sizeof(WCHAR);
    424  test.MaximumLength = test.Length;
    425 
    426  return MatchUnicodeString(test, &IsHexDigit);
    427 }
    428 
    429 inline bool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) {
    430  // Quick check: If the string is too short, don't bother
    431  // (We need 16 hex digits, one char for '.', and 3 for extension)
    432  const USHORT kMinLen = (16 + 1 + 3) * sizeof(wchar_t);
    433  if (aLeafName.Length < kMinLen) {
    434    return false;
    435  }
    436 
    437  uint16_t dotIndex;
    438  if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) {
    439    return false;
    440  }
    441 
    442  if (dotIndex < 16) {
    443    return false;
    444  }
    445 
    446  UNICODE_STRING test;
    447  test.Buffer = aLeafName.Buffer;
    448  test.Length = dotIndex * sizeof(WCHAR);
    449  test.MaximumLength = aLeafName.MaximumLength;
    450 
    451  return MatchUnicodeString(test, &IsHexDigit);
    452 }
    453 
    454 inline void GetLeafName(PUNICODE_STRING aDestString,
    455                        PCUNICODE_STRING aSrcString) {
    456  WCHAR* buf = aSrcString->Buffer;
    457  WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1];
    458  WCHAR* cur = end;
    459  while (cur >= buf) {
    460    if (*cur == L'\\') {
    461      break;
    462    }
    463 
    464    --cur;
    465  }
    466 
    467  // At this point, either cur points to the final backslash, or it points to
    468  // buf - 1. Either way, we're interested in cur + 1 as the desired buffer.
    469  aDestString->Buffer = cur + 1;
    470  aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR);
    471  aDestString->MaximumLength = aDestString->Length;
    472 }
    473 
    474 #endif  // !defined(MOZILLA_INTERNAL_API)
    475 
    476 #if defined(MOZILLA_INTERNAL_API)
    477 
    478 inline const nsDependentSubstring GetLeafName(const nsAString& aString) {
    479  auto it = aString.EndReading();
    480  size_t pos = aString.Length();
    481  while (it > aString.BeginReading()) {
    482    if (*(it - 1) == u'\\') {
    483      return Substring(aString, pos);
    484    }
    485 
    486    MOZ_ASSERT(pos > 0);
    487    --pos;
    488    --it;
    489  }
    490 
    491  return Substring(aString, 0);  // No backslash in the string
    492 }
    493 
    494 #endif  // defined(MOZILLA_INTERNAL_API)
    495 
    496 inline char EnsureLowerCaseASCII(char aChar) {
    497  if (aChar >= 'A' && aChar <= 'Z') {
    498    aChar -= 'A' - 'a';
    499  }
    500 
    501  return aChar;
    502 }
    503 
    504 inline int StricmpASCII(const char* aLeft, const char* aRight) {
    505  char curLeft, curRight;
    506 
    507  do {
    508    curLeft = EnsureLowerCaseASCII(*(aLeft++));
    509    curRight = EnsureLowerCaseASCII(*(aRight++));
    510  } while (curLeft && curLeft == curRight);
    511 
    512  return curLeft - curRight;
    513 }
    514 
    515 inline int StrcmpASCII(const char* aLeft, const char* aRight) {
    516  char curLeft, curRight;
    517 
    518  do {
    519    curLeft = *(aLeft++);
    520    curRight = *(aRight++);
    521  } while (curLeft && curLeft == curRight);
    522 
    523  return curLeft - curRight;
    524 }
    525 
    526 inline size_t StrlenASCII(const char* aStr) {
    527  size_t len = 0;
    528 
    529  while (*(aStr++)) {
    530    ++len;
    531  }
    532 
    533  return len;
    534 }
    535 
    536 struct CodeViewRecord70 {
    537  uint32_t signature;
    538  GUID pdbSignature;
    539  uint32_t pdbAge;
    540  // A UTF-8 string, according to
    541  // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
    542  char pdbFileName[1];
    543 };
    544 
    545 class MOZ_RAII PEHeaders final {
    546  /**
    547   * This structure is documented on MSDN as VS_VERSIONINFO, but is not present
    548   * in SDK headers because it cannot be specified as a C struct. The following
    549   * structure contains the fixed-length fields at the beginning of
    550   * VS_VERSIONINFO.
    551   */
    552  struct VS_VERSIONINFO_HEADER {
    553    WORD wLength;
    554    WORD wValueLength;
    555    WORD wType;
    556    WCHAR szKey[16];  // std::size(L"VS_VERSION_INFO")
    557    // Additional data goes here, aligned on a 4-byte boundary
    558  };
    559 
    560 public:
    561  // The lowest two bits of an HMODULE are used as flags. Stripping those bits
    562  // from the HMODULE yields the base address of the binary's memory mapping.
    563  // (See LoadLibraryEx docs on MSDN)
    564  template <typename T>
    565  static T HModuleToBaseAddr(HMODULE aModule) {
    566    return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(aModule) &
    567                               ~uintptr_t(3));
    568  }
    569 
    570  explicit PEHeaders(void* aBaseAddress)
    571      : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress)) {}
    572 
    573  explicit PEHeaders(HMODULE aModule)
    574      : PEHeaders(HModuleToBaseAddr<PIMAGE_DOS_HEADER>(aModule)) {}
    575 
    576  explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader)
    577      : mMzHeader(aMzHeader),
    578        mPeHeader(nullptr),
    579        mImageLimit(nullptr),
    580        mIsImportDirectoryTampered(false) {
    581    if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
    582      return;
    583    }
    584 
    585    mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew);
    586    if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) {
    587      return;
    588    }
    589 
    590    if (mPeHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
    591      return;
    592    }
    593 
    594    DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
    595    // This is a coarse-grained check to ensure that the image size is
    596    // reasonable. It we aren't big enough to contain headers, we have a
    597    // problem!
    598    if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
    599      return;
    600    }
    601 
    602    mImageLimit = RVAToPtrUnchecked<void*>(imageSize - 1UL);
    603 
    604    PIMAGE_DATA_DIRECTORY importDirEntry =
    605        GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT);
    606    if (!importDirEntry) {
    607      return;
    608    }
    609 
    610    mIsImportDirectoryTampered = (importDirEntry->VirtualAddress >= imageSize);
    611  }
    612 
    613  explicit operator bool() const { return !!mImageLimit; }
    614 
    615  /**
    616   * This overload computes absolute virtual addresses relative to the base
    617   * address of the binary.
    618   */
    619  template <typename T, typename R>
    620  T RVAToPtr(R aRva) const {
    621    return RVAToPtr<T>(mMzHeader, aRva);
    622  }
    623 
    624  /**
    625   * This overload computes a result by adding aRva to aBase, but also ensures
    626   * that the resulting pointer falls within the bounds of this binary's memory
    627   * mapping.
    628   */
    629  template <typename T, typename R>
    630  T RVAToPtr(void* aBase, R aRva) const {
    631    if (!mImageLimit) {
    632      return nullptr;
    633    }
    634 
    635    char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
    636    if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
    637        absAddress > reinterpret_cast<char*>(mImageLimit)) {
    638      return nullptr;
    639    }
    640 
    641    return reinterpret_cast<T>(absAddress);
    642  }
    643 
    644  Maybe<Range<const uint8_t>> GetBounds() const {
    645    if (!mImageLimit) {
    646      return Nothing();
    647    }
    648 
    649    auto base = reinterpret_cast<const uint8_t*>(mMzHeader);
    650    DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
    651    return Some(Range(base, imageSize));
    652  }
    653 
    654  DWORD GetFileCharacteristics() const {
    655    return mPeHeader ? mPeHeader->FileHeader.Characteristics : 0;
    656  }
    657 
    658  bool IsWithinImage(const void* aAddress) const {
    659    uintptr_t addr = reinterpret_cast<uintptr_t>(aAddress);
    660    uintptr_t imageBase = reinterpret_cast<uintptr_t>(mMzHeader);
    661    uintptr_t imageLimit = reinterpret_cast<uintptr_t>(mImageLimit);
    662    return addr >= imageBase && addr <= imageLimit;
    663  }
    664 
    665  PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const {
    666    // If the import directory is already tampered, we skip bounds check
    667    // because it could be located outside the mapped image.
    668    return mIsImportDirectoryTampered
    669               ? GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR,
    670                                        BoundsCheckPolicy::Skip>(
    671                     IMAGE_DIRECTORY_ENTRY_IMPORT)
    672               : GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>(
    673                     IMAGE_DIRECTORY_ENTRY_IMPORT);
    674  }
    675 
    676  PIMAGE_RESOURCE_DIRECTORY GetResourceTable() const {
    677    return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
    678        IMAGE_DIRECTORY_ENTRY_RESOURCE);
    679  }
    680 
    681  PIMAGE_DATA_DIRECTORY GetImageDirectoryEntryPtr(
    682      const uint32_t aDirectoryIndex, uint32_t* aOutRva = nullptr) const {
    683    if (aOutRva) {
    684      *aOutRva = 0;
    685    }
    686 
    687    IMAGE_OPTIONAL_HEADER& optionalHeader = mPeHeader->OptionalHeader;
    688 
    689    const uint32_t maxIndex = std::min(optionalHeader.NumberOfRvaAndSizes,
    690                                       DWORD(IMAGE_NUMBEROF_DIRECTORY_ENTRIES));
    691    if (aDirectoryIndex >= maxIndex) {
    692      return nullptr;
    693    }
    694 
    695    PIMAGE_DATA_DIRECTORY dirEntry =
    696        &optionalHeader.DataDirectory[aDirectoryIndex];
    697    if (aOutRva) {
    698      *aOutRva = reinterpret_cast<char*>(dirEntry) -
    699                 reinterpret_cast<char*>(mMzHeader);
    700      MOZ_ASSERT(*aOutRva);
    701    }
    702 
    703    return dirEntry;
    704  }
    705 
    706  bool GetVersionInfo(uint64_t& aOutVersion) const {
    707    // RT_VERSION == 16
    708    // Version resources require an id of 1
    709    auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
    710    if (!root) {
    711      return false;
    712    }
    713 
    714    VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root);
    715    if (!fixedInfo) {
    716      return false;
    717    }
    718 
    719    aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) |
    720                   static_cast<uint64_t>(fixedInfo->dwFileVersionLS));
    721    return true;
    722  }
    723 
    724  bool GetTimeStamp(DWORD& aResult) const {
    725    if (!(*this)) {
    726      return false;
    727    }
    728 
    729    aResult = mPeHeader->FileHeader.TimeDateStamp;
    730    return true;
    731  }
    732 
    733  bool GetImageSize(DWORD& aResult) const {
    734    if (!(*this)) {
    735      return false;
    736    }
    737 
    738    aResult = mPeHeader->OptionalHeader.SizeOfImage;
    739    return true;
    740  }
    741 
    742  bool GetCheckSum(DWORD& aResult) const {
    743    if (!(*this)) {
    744      return false;
    745    }
    746 
    747    aResult = mPeHeader->OptionalHeader.CheckSum;
    748    return true;
    749  }
    750 
    751  PIMAGE_IMPORT_DESCRIPTOR
    752  GetImportDescriptor(const char* aModuleNameASCII) const {
    753    for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
    754         IsValid(curImpDesc); ++curImpDesc) {
    755      auto curName = mIsImportDirectoryTampered
    756                         ? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
    757                         : RVAToPtr<const char*>(curImpDesc->Name);
    758      if (!curName) {
    759        return nullptr;
    760      }
    761 
    762      if (StricmpASCII(aModuleNameASCII, curName)) {
    763        continue;
    764      }
    765 
    766      // curImpDesc now points to the IAT for the module we're interested in
    767      return curImpDesc;
    768    }
    769 
    770    return nullptr;
    771  }
    772 
    773  template <typename CallbackT>
    774  void EnumImportChunks(const CallbackT& aCallback) const {
    775    for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
    776         IsValid(curImpDesc); ++curImpDesc) {
    777      auto curName = mIsImportDirectoryTampered
    778                         ? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
    779                         : RVAToPtr<const char*>(curImpDesc->Name);
    780      if (!curName) {
    781        continue;
    782      }
    783 
    784      aCallback(curName);
    785    }
    786  }
    787 
    788  /**
    789   * If |aBoundaries| is given, this method checks whether each IAT entry is
    790   * within the given range, and if any entry is out of the range, we return
    791   * Nothing().
    792   */
    793  Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule(
    794      const char* aModuleNameASCII,
    795      const Range<const uint8_t>* aBoundaries = nullptr) const {
    796    PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII);
    797    if (!impDesc) {
    798      return Nothing();
    799    }
    800 
    801    auto firstIatThunk =
    802        this->template RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk);
    803    if (!firstIatThunk) {
    804      return Nothing();
    805    }
    806 
    807    // Find the length by iterating through the table until we find a null entry
    808    PIMAGE_THUNK_DATA curIatThunk = firstIatThunk;
    809    while (IsValid(curIatThunk)) {
    810      if (aBoundaries) {
    811        auto iatEntry =
    812            reinterpret_cast<const uint8_t*>(curIatThunk->u1.Function);
    813        if (iatEntry < aBoundaries->begin().get() ||
    814            iatEntry >= aBoundaries->end().get()) {
    815          return Nothing();
    816        }
    817      }
    818 
    819      ++curIatThunk;
    820    }
    821 
    822    return Some(Span(firstIatThunk, curIatThunk));
    823  }
    824 
    825  /**
    826   * Resources are stored in a three-level tree. To locate a particular entry,
    827   * you must supply a resource type, the resource id, and then the language id.
    828   * If aLangId == 0, we just resolve the first entry regardless of language.
    829   */
    830  template <typename T>
    831  T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const {
    832    PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
    833    if (!topLevel) {
    834      return nullptr;
    835    }
    836 
    837    PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry =
    838        FindResourceEntry(topLevel, aType);
    839    if (!typeEntry || !typeEntry->DataIsDirectory) {
    840      return nullptr;
    841    }
    842 
    843    auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
    844        topLevel, typeEntry->OffsetToDirectory);
    845    PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId);
    846    if (!idEntry || !idEntry->DataIsDirectory) {
    847      return nullptr;
    848    }
    849 
    850    auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
    851        topLevel, idEntry->OffsetToDirectory);
    852    PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry;
    853    if (aLangId) {
    854      langEntry = FindResourceEntry(langDir, aLangId);
    855    } else {
    856      langEntry = FindFirstResourceEntry(langDir);
    857    }
    858 
    859    if (!langEntry || langEntry->DataIsDirectory) {
    860      return nullptr;
    861    }
    862 
    863    auto dataEntry =
    864        RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData);
    865    return dataEntry ? RVAToPtr<T>(dataEntry->OffsetToData) : nullptr;
    866  }
    867 
    868  template <size_t N>
    869  Maybe<Span<const uint8_t>> FindSection(const char (&aSecName)[N],
    870                                         DWORD aCharacteristicsMask) const {
    871    static_assert((N - 1) <= IMAGE_SIZEOF_SHORT_NAME,
    872                  "Section names must be at most 8 characters excluding null "
    873                  "terminator");
    874 
    875    if (!(*this)) {
    876      return Nothing();
    877    }
    878 
    879    Span<IMAGE_SECTION_HEADER> sectionTable = GetSectionTable();
    880    for (auto&& sectionHeader : sectionTable) {
    881      if (strncmp(reinterpret_cast<const char*>(sectionHeader.Name), aSecName,
    882                  IMAGE_SIZEOF_SHORT_NAME)) {
    883        continue;
    884      }
    885 
    886      if (!(sectionHeader.Characteristics & aCharacteristicsMask)) {
    887        // We found the section but it does not have the expected
    888        // characteristics
    889        return Nothing();
    890      }
    891 
    892      DWORD rva = sectionHeader.VirtualAddress;
    893      if (!rva) {
    894        return Nothing();
    895      }
    896 
    897      DWORD size = sectionHeader.Misc.VirtualSize;
    898      if (!size) {
    899        return Nothing();
    900      }
    901 
    902      auto base = RVAToPtr<const uint8_t*>(rva);
    903      return Some(Span(base, size));
    904    }
    905 
    906    return Nothing();
    907  }
    908 
    909  // There may be other code sections in the binary besides .text
    910  Maybe<Span<const uint8_t>> GetTextSectionInfo() const {
    911    return FindSection(".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE |
    912                                    IMAGE_SCN_MEM_READ);
    913  }
    914 
    915  // There may be other data sections in the binary besides .data
    916  Maybe<Span<const uint8_t>> GetDataSectionInfo() const {
    917    return FindSection(".data", IMAGE_SCN_CNT_INITIALIZED_DATA |
    918                                    IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE);
    919  }
    920 
    921  static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
    922    return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
    923  }
    924 
    925  static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
    926    return aImgThunk && aImgThunk->u1.Ordinal != 0;
    927  }
    928 
    929  bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered; }
    930 
    931  FARPROC GetEntryPoint() const {
    932    // Use the unchecked version because the entrypoint may be tampered.
    933    return RVAToPtrUnchecked<FARPROC>(
    934        mPeHeader->OptionalHeader.AddressOfEntryPoint);
    935  }
    936 
    937  const CodeViewRecord70* GetPdbInfo() const {
    938    PIMAGE_DEBUG_DIRECTORY debugDirectory =
    939        GetImageDirectoryEntry<PIMAGE_DEBUG_DIRECTORY>(
    940            IMAGE_DIRECTORY_ENTRY_DEBUG);
    941    if (!debugDirectory) {
    942      return nullptr;
    943    }
    944 
    945    const CodeViewRecord70* debugInfo =
    946        RVAToPtr<CodeViewRecord70*>(debugDirectory->AddressOfRawData);
    947    return (debugInfo && debugInfo->signature == 'SDSR') ? debugInfo : nullptr;
    948  }
    949 
    950 private:
    951  enum class BoundsCheckPolicy { Default, Skip };
    952 
    953  template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default>
    954  T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const {
    955    PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
    956    if (!dirEntry) {
    957      return nullptr;
    958    }
    959 
    960    return Policy == BoundsCheckPolicy::Skip
    961               ? RVAToPtrUnchecked<T>(dirEntry->VirtualAddress)
    962               : RVAToPtr<T>(dirEntry->VirtualAddress);
    963  }
    964 
    965  // This private variant does not have bounds checks, because we need to be
    966  // able to resolve the bounds themselves.
    967  template <typename T, typename R>
    968  T RVAToPtrUnchecked(R aRva) const {
    969    return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
    970  }
    971 
    972  Span<IMAGE_SECTION_HEADER> GetSectionTable() const {
    973    MOZ_ASSERT(*this);
    974    auto base = RVAToPtr<PIMAGE_SECTION_HEADER>(
    975        &mPeHeader->OptionalHeader, mPeHeader->FileHeader.SizeOfOptionalHeader);
    976    // The Windows loader has an internal limit of 96 sections (per PE spec)
    977    auto numSections =
    978        std::min(mPeHeader->FileHeader.NumberOfSections, WORD(96));
    979    return Span{base, numSections};
    980  }
    981 
    982  PIMAGE_RESOURCE_DIRECTORY_ENTRY
    983  FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const {
    984    if (!aCurLevel) {
    985      return nullptr;
    986    }
    987 
    988    // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
    989    // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
    990    // searches by ID, we need to skip past any named entries before iterating.
    991    auto dirEnt =
    992        reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
    993        aCurLevel->NumberOfNamedEntries;
    994    if (!(IsWithinImage(dirEnt) &&
    995          IsWithinImage(&dirEnt[aCurLevel->NumberOfIdEntries - 1].Id))) {
    996      return nullptr;
    997    }
    998 
    999    for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
   1000      if (dirEnt[i].Id == aId) {
   1001        return &dirEnt[i];
   1002      }
   1003    }
   1004 
   1005    return nullptr;
   1006  }
   1007 
   1008  PIMAGE_RESOURCE_DIRECTORY_ENTRY
   1009  FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const {
   1010    // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
   1011    // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
   1012    // entry, regardless of whether it is indexed by name or by id.
   1013    auto dirEnt =
   1014        reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
   1015    WORD numEntries =
   1016        aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries;
   1017    if (!numEntries) {
   1018      return nullptr;
   1019    }
   1020 
   1021    return dirEnt;
   1022  }
   1023 
   1024  VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
   1025    WORD length = aVerInfo->wLength;
   1026    if (length < sizeof(VS_VERSIONINFO_HEADER)) {
   1027      return nullptr;
   1028    }
   1029 
   1030    const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
   1031    if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
   1032                           std::size(kVersionInfoKey)) !=
   1033        std::size(kVersionInfoKey)) {
   1034      return nullptr;
   1035    }
   1036 
   1037    if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) {
   1038      // Fixed file info does not exist
   1039      return nullptr;
   1040    }
   1041 
   1042    WORD offset = sizeof(VS_VERSIONINFO_HEADER);
   1043 
   1044    uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
   1045    // Align up to 4-byte boundary
   1046 #pragma warning(suppress : 4146)
   1047    offset += (-(base + offset) & 3);
   1048 
   1049    if (offset >= length) {
   1050      return nullptr;
   1051    }
   1052 
   1053    auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
   1054    if (result->dwSignature != 0xFEEF04BD) {
   1055      return nullptr;
   1056    }
   1057 
   1058    return result;
   1059  }
   1060 
   1061 private:
   1062  PIMAGE_DOS_HEADER mMzHeader;
   1063  PIMAGE_NT_HEADERS mPeHeader;
   1064  void* mImageLimit;
   1065  bool mIsImportDirectoryTampered;
   1066 };
   1067 
   1068 // This class represents an export section of a local/remote process.
   1069 template <typename MMPolicy>
   1070 class MOZ_RAII PEExportSection {
   1071  const MMPolicy& mMMPolicy;
   1072  uintptr_t mImageBase;
   1073  DWORD mOrdinalBase;
   1074  DWORD mRvaDirStart;
   1075  DWORD mRvaDirEnd;
   1076  mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportAddressTable;
   1077  mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportNameTable;
   1078  mozilla::interceptor::TargetObjectArray<MMPolicy, WORD> mExportOrdinalTable;
   1079 
   1080  explicit PEExportSection(const MMPolicy& aMMPolicy)
   1081      : mMMPolicy(aMMPolicy),
   1082        mImageBase(0),
   1083        mOrdinalBase(0),
   1084        mRvaDirStart(0),
   1085        mRvaDirEnd(0),
   1086        mExportAddressTable(mMMPolicy),
   1087        mExportNameTable(mMMPolicy),
   1088        mExportOrdinalTable(mMMPolicy) {}
   1089 
   1090  PEExportSection(const MMPolicy& aMMPolicy, uintptr_t aImageBase,
   1091                  DWORD aRvaDirStart, DWORD aRvaDirEnd,
   1092                  const IMAGE_EXPORT_DIRECTORY& exportDir)
   1093      : mMMPolicy(aMMPolicy),
   1094        mImageBase(aImageBase),
   1095        mOrdinalBase(exportDir.Base),
   1096        mRvaDirStart(aRvaDirStart),
   1097        mRvaDirEnd(aRvaDirEnd),
   1098        mExportAddressTable(mMMPolicy,
   1099                            mImageBase + exportDir.AddressOfFunctions,
   1100                            exportDir.NumberOfFunctions),
   1101        mExportNameTable(mMMPolicy, mImageBase + exportDir.AddressOfNames,
   1102                         exportDir.NumberOfNames),
   1103        mExportOrdinalTable(mMMPolicy,
   1104                            mImageBase + exportDir.AddressOfNameOrdinals,
   1105                            exportDir.NumberOfNames) {}
   1106 
   1107  static const PEExportSection Get(uintptr_t aImageBase,
   1108                                   const MMPolicy& aMMPolicy) {
   1109    mozilla::interceptor::TargetObject<MMPolicy, IMAGE_DOS_HEADER> mzHeader(
   1110        aMMPolicy, aImageBase);
   1111    if (!mzHeader || mzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
   1112      return PEExportSection(aMMPolicy);
   1113    }
   1114 
   1115    mozilla::interceptor::TargetObject<MMPolicy, IMAGE_NT_HEADERS> peHeader(
   1116        aMMPolicy, aImageBase + mzHeader->e_lfanew);
   1117    if (!peHeader || peHeader->Signature != IMAGE_NT_SIGNATURE) {
   1118      return PEExportSection(aMMPolicy);
   1119    }
   1120 
   1121    if (peHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
   1122      return PEExportSection(aMMPolicy);
   1123    }
   1124 
   1125    const IMAGE_OPTIONAL_HEADER& optionalHeader = peHeader->OptionalHeader;
   1126 
   1127    DWORD imageSize = optionalHeader.SizeOfImage;
   1128    // This is a coarse-grained check to ensure that the image size is
   1129    // reasonable. It we aren't big enough to contain headers, we have a
   1130    // problem!
   1131    if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
   1132      return PEExportSection(aMMPolicy);
   1133    }
   1134 
   1135    if (optionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) {
   1136      return PEExportSection(aMMPolicy);
   1137    }
   1138 
   1139    const IMAGE_DATA_DIRECTORY& exportDirectoryEntry =
   1140        optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
   1141    if (!exportDirectoryEntry.VirtualAddress || !exportDirectoryEntry.Size) {
   1142      return PEExportSection(aMMPolicy);
   1143    }
   1144 
   1145    mozilla::interceptor::TargetObject<MMPolicy, IMAGE_EXPORT_DIRECTORY>
   1146        exportDirectory(aMMPolicy,
   1147                        aImageBase + exportDirectoryEntry.VirtualAddress);
   1148    if (!exportDirectory || !exportDirectory->NumberOfFunctions) {
   1149      return PEExportSection(aMMPolicy);
   1150    }
   1151 
   1152    return PEExportSection(
   1153        aMMPolicy, aImageBase, exportDirectoryEntry.VirtualAddress,
   1154        exportDirectoryEntry.VirtualAddress + exportDirectoryEntry.Size,
   1155        *exportDirectory.GetLocalBase());
   1156  }
   1157 
   1158  FARPROC GetProcAddressByOrdinal(WORD aOrdinal) const {
   1159    if (aOrdinal < mOrdinalBase) {
   1160      return nullptr;
   1161    }
   1162 
   1163    auto rvaToFunction = mExportAddressTable[aOrdinal - mOrdinalBase];
   1164    if (!rvaToFunction) {
   1165      return nullptr;
   1166    }
   1167    return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
   1168  }
   1169 
   1170 public:
   1171  static const PEExportSection Get(HMODULE aModule, const MMPolicy& aMMPolicy) {
   1172    return Get(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule), aMMPolicy);
   1173  }
   1174 
   1175  explicit operator bool() const {
   1176    // Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified
   1177    // mMMPolicy is expected to be false.  We don't check mMMPolicy here.
   1178    return mImageBase && mRvaDirStart && mRvaDirEnd && mExportAddressTable &&
   1179           mExportNameTable && mExportOrdinalTable;
   1180  }
   1181 
   1182  template <typename T>
   1183  T RVAToPtr(uint32_t aRva) const {
   1184    return reinterpret_cast<T>(mImageBase + aRva);
   1185  }
   1186 
   1187  PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const {
   1188    if (!*this) {
   1189      return nullptr;
   1190    }
   1191 
   1192    return RVAToPtr<PIMAGE_EXPORT_DIRECTORY>(mRvaDirStart);
   1193  }
   1194 
   1195  /**
   1196   * This functions searches the export table for a given string as
   1197   * GetProcAddress does, but this returns a matched entry of the Export
   1198   * Address Table i.e. a pointer to an RVA of a matched function instead
   1199   * of a function address.  If the entry is forwarded, this function
   1200   * returns nullptr.
   1201   */
   1202  const DWORD* FindExportAddressTableEntry(
   1203      const char* aFunctionNameASCII) const {
   1204    if (!*this || !aFunctionNameASCII) {
   1205      return nullptr;
   1206    }
   1207 
   1208    struct NameTableComparator {
   1209      NameTableComparator(const PEExportSection<MMPolicy>& aExportSection,
   1210                          const char* aTarget)
   1211          : mExportSection(aExportSection),
   1212            mTargetName(aTarget),
   1213            mTargetNamelength(StrlenASCII(aTarget)) {}
   1214 
   1215      int operator()(DWORD aRVAToString) const {
   1216        mozilla::interceptor::TargetObjectArray<MMPolicy, char> itemString(
   1217            mExportSection.mMMPolicy, mExportSection.mImageBase + aRVAToString,
   1218            mTargetNamelength + 1);
   1219        return StrcmpASCII(mTargetName, itemString[0]);
   1220      }
   1221 
   1222      const PEExportSection<MMPolicy>& mExportSection;
   1223      const char* mTargetName;
   1224      size_t mTargetNamelength;
   1225    };
   1226 
   1227    const NameTableComparator comp(*this, aFunctionNameASCII);
   1228 
   1229    size_t match;
   1230    if (!mExportNameTable.BinarySearchIf(comp, &match)) {
   1231      return nullptr;
   1232    }
   1233 
   1234    const WORD* index = mExportOrdinalTable[match];
   1235    if (!index) {
   1236      return nullptr;
   1237    }
   1238 
   1239    const DWORD* rvaToFunction = mExportAddressTable[*index];
   1240    if (!rvaToFunction) {
   1241      return nullptr;
   1242    }
   1243 
   1244    if (*rvaToFunction >= mRvaDirStart && *rvaToFunction < mRvaDirEnd) {
   1245      // If an entry points to an address within the export section, the
   1246      // field is a forwarder RVA.  We return nullptr because the entry is
   1247      // not a function address but a null-terminated string used for export
   1248      // forwarding.
   1249      return nullptr;
   1250    }
   1251 
   1252    return rvaToFunction;
   1253  }
   1254 
   1255  /**
   1256   * This functions behaves the same as the native ::GetProcAddress except
   1257   * the following cases:
   1258   * - Returns nullptr if a target entry is forwarded to another dll.
   1259   */
   1260  FARPROC GetProcAddress(const char* aFunctionNameASCII) const {
   1261    uintptr_t maybeOdrinal = reinterpret_cast<uintptr_t>(aFunctionNameASCII);
   1262    // When the high-order word of |aFunctionNameASCII| is zero, it's not
   1263    // a string but an ordinal value.
   1264    if (maybeOdrinal < 0x10000) {
   1265      return GetProcAddressByOrdinal(static_cast<WORD>(maybeOdrinal));
   1266    }
   1267 
   1268    auto rvaToFunction = FindExportAddressTableEntry(aFunctionNameASCII);
   1269    if (!rvaToFunction) {
   1270      return nullptr;
   1271    }
   1272    return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
   1273  }
   1274 };
   1275 
   1276 inline HANDLE RtlGetProcessHeap() {
   1277  PTEB teb = ::NtCurrentTeb();
   1278  PPEB peb = teb->ProcessEnvironmentBlock;
   1279  return peb->Reserved4[1];
   1280 }
   1281 
   1282 inline PVOID RtlGetThreadLocalStoragePointer() {
   1283  return ::NtCurrentTeb()->Reserved1[11];
   1284 }
   1285 
   1286 inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue) {
   1287  ::NtCurrentTeb()->Reserved1[11] = aNewValue;
   1288 }
   1289 
   1290 inline DWORD RtlGetCurrentThreadId() {
   1291  PTEB teb = ::NtCurrentTeb();
   1292  CLIENT_ID* cid = reinterpret_cast<CLIENT_ID*>(&teb->Reserved1[8]);
   1293  return static_cast<DWORD>(reinterpret_cast<uintptr_t>(cid->UniqueThread) &
   1294                            0xFFFFFFFFUL);
   1295 }
   1296 
   1297 inline PVOID RtlGetThreadStackBase() {
   1298  return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackBase;
   1299 }
   1300 
   1301 inline PVOID RtlGetThreadStackLimit() {
   1302  return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackLimit;
   1303 }
   1304 
   1305 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
   1306 
   1307 inline LauncherResult<DWORD> GetParentProcessId() {
   1308  struct PROCESS_BASIC_INFORMATION {
   1309    NTSTATUS ExitStatus;
   1310    PPEB PebBaseAddress;
   1311    ULONG_PTR AffinityMask;
   1312    LONG BasePriority;
   1313    ULONG_PTR UniqueProcessId;
   1314    ULONG_PTR InheritedFromUniqueProcessId;
   1315  };
   1316 
   1317  ULONG returnLength;
   1318  PROCESS_BASIC_INFORMATION pbi = {};
   1319  NTSTATUS status =
   1320      ::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation,
   1321                                  &pbi, sizeof(pbi), &returnLength);
   1322  if (!NT_SUCCESS(status)) {
   1323    return LAUNCHER_ERROR_FROM_NTSTATUS(status);
   1324  }
   1325 
   1326  return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
   1327 }
   1328 
   1329 inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
   1330                                    PMEMORY_BASIC_INFORMATION aMemInfo,
   1331                                    SIZE_T aMemInfoLen) {
   1332 #if defined(MOZILLA_INTERNAL_API)
   1333  return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
   1334 #else
   1335  SIZE_T returnedLength;
   1336  NTSTATUS status = ::NtQueryVirtualMemory(
   1337      aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
   1338      aMemInfoLen, &returnedLength);
   1339  if (!NT_SUCCESS(status)) {
   1340    ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
   1341    returnedLength = 0;
   1342  }
   1343  return returnedLength;
   1344 #endif  // defined(MOZILLA_INTERNAL_API)
   1345 }
   1346 
   1347 inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
   1348                                  PMEMORY_BASIC_INFORMATION aMemInfo,
   1349                                  SIZE_T aMemInfoLen) {
   1350  return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
   1351 }
   1352 
   1353 struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
   1354  DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
   1355 
   1356  MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther)
   1357      : _IMAGE_DATA_DIRECTORY(aOther) {}
   1358 
   1359  DataDirectoryEntry(const DataDirectoryEntry& aOther) = default;
   1360 
   1361  bool operator==(const DataDirectoryEntry& aOther) const {
   1362    return VirtualAddress == aOther.VirtualAddress && Size == aOther.Size;
   1363  }
   1364 
   1365  bool operator!=(const DataDirectoryEntry& aOther) const {
   1366    return !(*this == aOther);
   1367  }
   1368 };
   1369 
   1370 inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) {
   1371  ULONG returnLength;
   1372  PROCESS_BASIC_INFORMATION pbi;
   1373  NTSTATUS status = ::NtQueryInformationProcess(
   1374      aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
   1375  if (!NT_SUCCESS(status)) {
   1376    return LAUNCHER_ERROR_FROM_NTSTATUS(status);
   1377  }
   1378 
   1379  return pbi.PebBaseAddress;
   1380 }
   1381 
   1382 /**
   1383 * This function relies on a specific offset into the mostly-undocumented PEB
   1384 * structure. The risk is reduced thanks to the fact that the Chromium sandbox
   1385 * relies on the location of this field. It is unlikely to change at this point.
   1386 * To further reduce the risk, we also check for the magic 'MZ' signature that
   1387 * should indicate the beginning of a PE image.
   1388 */
   1389 inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
   1390  LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess);
   1391  if (ppeb.isErr()) {
   1392    return ppeb.propagateErr();
   1393  }
   1394 
   1395  PEB peb;
   1396  SIZE_T bytesRead;
   1397 
   1398 #if defined(MOZILLA_INTERNAL_API)
   1399  if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb),
   1400                           &bytesRead) ||
   1401      bytesRead != sizeof(peb)) {
   1402    return LAUNCHER_ERROR_FROM_LAST();
   1403  }
   1404 #else
   1405  NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb,
   1406                                            sizeof(peb), &bytesRead);
   1407  if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) {
   1408    return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
   1409  }
   1410 #endif
   1411 
   1412  // peb.ImageBaseAddress
   1413  void* baseAddress = peb.Reserved3[1];
   1414 
   1415  char mzMagic[2];
   1416 #if defined(MOZILLA_INTERNAL_API)
   1417  if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic),
   1418                           &bytesRead) ||
   1419      bytesRead != sizeof(mzMagic)) {
   1420    return LAUNCHER_ERROR_FROM_LAST();
   1421  }
   1422 #else
   1423  ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic,
   1424                                   sizeof(mzMagic), &bytesRead);
   1425  if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) {
   1426    return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
   1427  }
   1428 #endif
   1429 
   1430  MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z');
   1431  if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') {
   1432    return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
   1433  }
   1434 
   1435  return static_cast<HMODULE>(baseAddress);
   1436 }
   1437 
   1438 #if defined(_MSC_VER)
   1439 extern "C" IMAGE_DOS_HEADER __ImageBase;
   1440 #endif
   1441 
   1442 // This class manages data transfer from the local process's executable
   1443 // to another process's executable via WriteProcessMemory.
   1444 // Bug 1662560 told us the same executable may be mapped onto a different
   1445 // address in a different process.  This means when we transfer data within
   1446 // the mapped executable such as a global variable or IAT from the current
   1447 // process to another process, we need to shift its address by the difference
   1448 // between two executable's mapped imagebase.
   1449 class CrossExecTransferManager final {
   1450  HANDLE mRemoteProcess;
   1451  uint8_t* mLocalImagebase;
   1452  PEHeaders mLocalExec;
   1453  uint8_t* mRemoteImagebase;
   1454 
   1455  static HMODULE GetLocalExecModule() {
   1456 #if defined(_MSC_VER)
   1457    return reinterpret_cast<HMODULE>(&__ImageBase);
   1458 #else
   1459    return ::GetModuleHandleW(nullptr);
   1460 #endif
   1461  }
   1462 
   1463  LauncherVoidResult EnsureRemoteImagebase() {
   1464    if (!mRemoteImagebase) {
   1465      LauncherResult<HMODULE> remoteImageBaseResult =
   1466          GetProcessExeModule(mRemoteProcess);
   1467      if (remoteImageBaseResult.isErr()) {
   1468        return remoteImageBaseResult.propagateErr();
   1469      }
   1470 
   1471      mRemoteImagebase =
   1472          reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap());
   1473    }
   1474    return Ok();
   1475  }
   1476 
   1477  template <typename T>
   1478  T* LocalExecToRemoteExec(T* aLocalAddress) const {
   1479    MOZ_ASSERT(mRemoteImagebase);
   1480    MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress));
   1481 
   1482    if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) {
   1483      return aLocalAddress;
   1484    }
   1485 
   1486    uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) -
   1487                       reinterpret_cast<uintptr_t>(mLocalImagebase);
   1488    return reinterpret_cast<T*>(mRemoteImagebase + offset);
   1489  }
   1490 
   1491 public:
   1492  explicit CrossExecTransferManager(HANDLE aRemoteProcess)
   1493      : mRemoteProcess(aRemoteProcess),
   1494        mLocalImagebase(
   1495            PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())),
   1496        mLocalExec(mLocalImagebase),
   1497        mRemoteImagebase(nullptr) {}
   1498 
   1499  CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase)
   1500      : mRemoteProcess(aRemoteProcess),
   1501        mLocalImagebase(
   1502            PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)),
   1503        mLocalExec(mLocalImagebase),
   1504        mRemoteImagebase(nullptr) {}
   1505 
   1506  explicit operator bool() const { return !!mLocalExec; }
   1507  HANDLE RemoteProcess() const { return mRemoteProcess; }
   1508  const PEHeaders& LocalPEHeaders() const { return mLocalExec; }
   1509 
   1510  AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength,
   1511                             DWORD aProtFlags) {
   1512    // If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
   1513    (void)EnsureRemoteImagebase();
   1514    return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength,
   1515                              aProtFlags, mRemoteProcess);
   1516  }
   1517 
   1518  LauncherVoidResult Transfer(LPVOID aDestinationAddress,
   1519                              LPCVOID aBufferToWrite, SIZE_T aBufferSize) {
   1520    LauncherVoidResult result = EnsureRemoteImagebase();
   1521    if (result.isErr()) {
   1522      return result.propagateErr();
   1523    }
   1524 
   1525    if (!::WriteProcessMemory(mRemoteProcess,
   1526                              LocalExecToRemoteExec(aDestinationAddress),
   1527                              aBufferToWrite, aBufferSize, nullptr)) {
   1528      return LAUNCHER_ERROR_FROM_LAST();
   1529    }
   1530 
   1531    return Ok();
   1532  }
   1533 };
   1534 
   1535 #if !defined(MOZILLA_INTERNAL_API)
   1536 
   1537 inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
   1538    const UNICODE_STRING& aTarget) {
   1539  auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess);
   1540  if (maybePeb.isErr()) {
   1541    return maybePeb.propagateErr();
   1542  }
   1543 
   1544  const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap());
   1545  if (!peb->Ldr) {
   1546    return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
   1547  }
   1548 
   1549  auto firstItem = &peb->Ldr->InMemoryOrderModuleList;
   1550  for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) {
   1551    const auto currentTableEntry =
   1552        CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
   1553 
   1554    UNICODE_STRING leafName;
   1555    nt::GetLeafName(&leafName, &currentTableEntry->FullDllName);
   1556 
   1557    if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) {
   1558      return reinterpret_cast<HMODULE>(currentTableEntry->DllBase);
   1559    }
   1560  }
   1561 
   1562  return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND);
   1563 }
   1564 
   1565 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final {
   1566 public:
   1567  constexpr SRWLock() : mLock(SRWLOCK_INIT) {}
   1568 
   1569  void LockShared() { ::RtlAcquireSRWLockShared(&mLock); }
   1570 
   1571  void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock); }
   1572 
   1573  void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock); }
   1574 
   1575  void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock); }
   1576 
   1577  SRWLock(const SRWLock&) = delete;
   1578  SRWLock(SRWLock&&) = delete;
   1579  SRWLock& operator=(const SRWLock&) = delete;
   1580  SRWLock& operator=(SRWLock&&) = delete;
   1581 
   1582  SRWLOCK* operator&() { return &mLock; }
   1583 
   1584 private:
   1585  SRWLOCK mLock;
   1586 };
   1587 
   1588 class MOZ_RAII AutoExclusiveLock final {
   1589 public:
   1590  explicit AutoExclusiveLock(SRWLock& aLock) : mLock(aLock) {
   1591    aLock.LockExclusive();
   1592  }
   1593 
   1594  ~AutoExclusiveLock() { mLock.UnlockExclusive(); }
   1595 
   1596  AutoExclusiveLock(const AutoExclusiveLock&) = delete;
   1597  AutoExclusiveLock(AutoExclusiveLock&&) = delete;
   1598  AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
   1599  AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
   1600 
   1601 private:
   1602  SRWLock& mLock;
   1603 };
   1604 
   1605 class MOZ_RAII AutoSharedLock final {
   1606 public:
   1607  explicit AutoSharedLock(SRWLock& aLock) : mLock(aLock) { aLock.LockShared(); }
   1608 
   1609  ~AutoSharedLock() { mLock.UnlockShared(); }
   1610 
   1611  AutoSharedLock(const AutoSharedLock&) = delete;
   1612  AutoSharedLock(AutoSharedLock&&) = delete;
   1613  AutoSharedLock& operator=(const AutoSharedLock&) = delete;
   1614  AutoSharedLock& operator=(AutoSharedLock&&) = delete;
   1615 
   1616 private:
   1617  SRWLock& mLock;
   1618 };
   1619 
   1620 #endif  // !defined(MOZILLA_INTERNAL_API)
   1621 
   1622 class RtlAllocPolicy {
   1623 public:
   1624  template <typename T>
   1625  T* maybe_pod_malloc(size_t aNumElems) {
   1626    size_t size;
   1627    if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
   1628      return nullptr;
   1629    }
   1630 
   1631    return static_cast<T*>(::RtlAllocateHeap(RtlGetProcessHeap(), 0, size));
   1632  }
   1633 
   1634  template <typename T>
   1635  T* maybe_pod_calloc(size_t aNumElems) {
   1636    size_t size;
   1637    if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) {
   1638      return nullptr;
   1639    }
   1640 
   1641    return static_cast<T*>(
   1642        ::RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, size));
   1643  }
   1644 
   1645  template <typename T>
   1646  T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
   1647    size_t size;
   1648    if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size))) {
   1649      return nullptr;
   1650    }
   1651 
   1652    return static_cast<T*>(
   1653        ::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr, size));
   1654  }
   1655 
   1656  template <typename T>
   1657  T* pod_malloc(size_t aNumElems) {
   1658    return maybe_pod_malloc<T>(aNumElems);
   1659  }
   1660 
   1661  template <typename T>
   1662  T* pod_calloc(size_t aNumElems) {
   1663    return maybe_pod_calloc<T>(aNumElems);
   1664  }
   1665 
   1666  template <typename T>
   1667  T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
   1668    return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
   1669  }
   1670 
   1671  template <typename T>
   1672  void free_(T* aPtr, size_t aNumElems = 0) {
   1673    ::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr);
   1674  }
   1675 
   1676  void reportAllocOverflow() const {}
   1677 
   1678  [[nodiscard]] bool checkSimulatedOOM() const { return true; }
   1679 };
   1680 
   1681 class AutoMappedView final {
   1682  void* mView;
   1683 
   1684  void Unmap() {
   1685    if (!mView) {
   1686      return;
   1687    }
   1688 
   1689 #if defined(MOZILLA_INTERNAL_API)
   1690    ::UnmapViewOfFile(mView);
   1691 #else
   1692    NTSTATUS status = ::NtUnmapViewOfSection(nt::kCurrentProcess, mView);
   1693    if (!NT_SUCCESS(status)) {
   1694      ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
   1695    }
   1696 #endif
   1697    mView = nullptr;
   1698  }
   1699 
   1700 public:
   1701  explicit AutoMappedView(void* aView) : mView(aView) {}
   1702 
   1703  AutoMappedView(HANDLE aSection, ULONG aProtectionFlags) : mView(nullptr) {
   1704 #if defined(MOZILLA_INTERNAL_API)
   1705    mView = ::MapViewOfFile(aSection, aProtectionFlags, 0, 0, 0);
   1706 #else
   1707    SIZE_T viewSize = 0;
   1708    NTSTATUS status = ::NtMapViewOfSection(aSection, nt::kCurrentProcess,
   1709                                           &mView, 0, 0, nullptr, &viewSize,
   1710                                           ViewUnmap, 0, aProtectionFlags);
   1711    if (!NT_SUCCESS(status)) {
   1712      ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
   1713    }
   1714 #endif
   1715  }
   1716  ~AutoMappedView() { Unmap(); }
   1717 
   1718  // Allow move & Disallow copy
   1719  AutoMappedView(AutoMappedView&& aOther) : mView(aOther.mView) {
   1720    aOther.mView = nullptr;
   1721  }
   1722  AutoMappedView& operator=(AutoMappedView&& aOther) {
   1723    if (this != &aOther) {
   1724      Unmap();
   1725      mView = aOther.mView;
   1726      aOther.mView = nullptr;
   1727    }
   1728    return *this;
   1729  }
   1730  AutoMappedView(const AutoMappedView&) = delete;
   1731  AutoMappedView& operator=(const AutoMappedView&) = delete;
   1732 
   1733  explicit operator bool() const { return !!mView; }
   1734  template <typename T>
   1735  T* as() {
   1736    return reinterpret_cast<T*>(mView);
   1737  }
   1738 
   1739  void* release() {
   1740    void* p = mView;
   1741    mView = nullptr;
   1742    return p;
   1743  }
   1744 };
   1745 
   1746 #if defined(_M_X64)
   1747 // CheckStack ensures that stack memory pages are committed up to a given size
   1748 // in bytes from the current stack pointer. It updates the thread stack limit,
   1749 // which points to the lowest committed stack address.
   1750 MOZ_NEVER_INLINE MOZ_NAKED inline void CheckStack(uint32_t size) {
   1751  asm volatile(
   1752      "mov %ecx, %eax;"
   1753 #  if defined(__MINGW32__)
   1754      "jmp ___chkstk_ms;"
   1755 #  else
   1756      "jmp __chkstk;"
   1757 #  endif  // __MINGW32__
   1758  );
   1759 }
   1760 #endif  // _M_X64
   1761 
   1762 }  // namespace nt
   1763 }  // namespace mozilla
   1764 
   1765 #endif  // mozilla_NativeNt_h