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