tor-browser

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

TestNativeNt.cpp (20661B)


      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 #include "nscore.h"
      8 #include "mozilla/NativeNt.h"
      9 #include "mozilla/ThreadLocal.h"
     10 #include "mozilla/UniquePtr.h"
     11 #include "mozilla/WindowsEnumProcessModules.h"
     12 
     13 #include <limits>
     14 #include <stdio.h>
     15 #include <windows.h>
     16 #include <strsafe.h>
     17 
     18 const wchar_t kNormal[] = L"Foo.dll";
     19 const wchar_t kHex12[] = L"Foo.ABCDEF012345.dll";
     20 const wchar_t kHex15[] = L"ABCDEF012345678.dll";
     21 const wchar_t kHex16[] = L"ABCDEF0123456789.dll";
     22 const wchar_t kHex17[] = L"ABCDEF0123456789a.dll";
     23 const wchar_t kHex24[] = L"ABCDEF0123456789cdabef98.dll";
     24 const wchar_t kHex8[] = L"01234567.dll";
     25 const wchar_t kNonHex12[] = L"Foo.ABCDEFG12345.dll";
     26 const wchar_t kHex13[] = L"Foo.ABCDEF0123456.dll";
     27 const wchar_t kHex11[] = L"Foo.ABCDEF01234.dll";
     28 const wchar_t kPrefixedHex16[] = L"Pabcdef0123456789.dll";
     29 const uint32_t kTlsDataValue = 1234;
     30 static MOZ_THREAD_LOCAL(uint32_t) sTlsData;
     31 
     32 // Need non-inline functions to bypass compiler optimization that the thread
     33 // local storage pointer is cached in a register before accessing a thread-local
     34 // variable. See bug 1803322 for a motivating example.
     35 MOZ_NEVER_INLINE uint32_t getTlsData() { return sTlsData.get(); }
     36 MOZ_NEVER_INLINE void setTlsData(uint32_t x) { sTlsData.set(x); }
     37 
     38 const char kFailFmt[] =
     39    "TEST-FAILED | NativeNt | %s(%s) should have returned %s but did not\n";
     40 
     41 #define RUN_TEST(fn, varName, expected)         \
     42  if (fn(varName) == !expected) {               \
     43    printf(kFailFmt, #fn, #varName, #expected); \
     44    return 1;                                   \
     45  }
     46 
     47 #define EXPECT_FAIL(fn, varName) RUN_TEST(fn, varName, false)
     48 
     49 #define EXPECT_SUCCESS(fn, varName) RUN_TEST(fn, varName, true)
     50 
     51 using namespace mozilla;
     52 using namespace mozilla::nt;
     53 
     54 bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) {
     55  MEMORY_BASIC_INFORMATION info1 = {}, info2 = {};
     56  SIZE_T result1 = ::VirtualQueryEx(aProcess, aAddress, &info1, sizeof(info1)),
     57         result2 = mozilla::nt::VirtualQueryEx(aProcess, aAddress, &info2,
     58                                               sizeof(info2));
     59  if (result1 != result2) {
     60    printf("TEST-FAILED | NativeNt | The returned values mismatch\n");
     61    return false;
     62  }
     63 
     64  if (!result1) {
     65    // Both APIs failed.
     66    return true;
     67  }
     68 
     69  if (memcmp(&info1, &info2, result1) != 0) {
     70    printf("TEST-FAILED | NativeNt | The returned structures mismatch\n");
     71    return false;
     72  }
     73 
     74  return true;
     75 }
     76 
     77 // This class copies the self executable file to the %temp%\<outer>\<inner>
     78 // folder.  The length of its path is longer than MAX_PATH.
     79 class LongNameModule {
     80  wchar_t mOuterDirBuffer[MAX_PATH];
     81  wchar_t mInnerDirBuffer[MAX_PATH * 2];
     82  wchar_t mTargetFileBuffer[MAX_PATH * 2];
     83 
     84  const wchar_t* mOuterDir;
     85  const wchar_t* mInnerDir;
     86  const wchar_t* mTargetFile;
     87 
     88 public:
     89  explicit LongNameModule(const wchar_t* aNewLeafNameAfterCopy)
     90      : mOuterDir(nullptr), mInnerDir(nullptr), mTargetFile(nullptr) {
     91    const wchar_t kFolderName160Chars[] =
     92        L"0123456789ABCDEF0123456789ABCDEF"
     93        L"0123456789ABCDEF0123456789ABCDEF"
     94        L"0123456789ABCDEF0123456789ABCDEF"
     95        L"0123456789ABCDEF0123456789ABCDEF"
     96        L"0123456789ABCDEF0123456789ABCDEF";
     97    UniquePtr<wchar_t[]> thisExe = GetFullBinaryPath();
     98    if (!thisExe) {
     99      return;
    100    }
    101 
    102    // If the buffer is too small, GetTempPathW returns the required
    103    // length including a null character, while on a successful case
    104    // it returns the number of copied characters which does not include
    105    // a null character.  This means len == MAX_PATH should never happen
    106    // and len > MAX_PATH means GetTempPathW failed.
    107    wchar_t tempDir[MAX_PATH];
    108    DWORD len = ::GetTempPathW(MAX_PATH, tempDir);
    109    if (!len || len >= MAX_PATH) {
    110      return;
    111    }
    112 
    113    if (FAILED(::StringCbPrintfW(mOuterDirBuffer, sizeof(mOuterDirBuffer),
    114                                 L"\\\\?\\%s%s", tempDir,
    115                                 kFolderName160Chars)) ||
    116        !::CreateDirectoryW(mOuterDirBuffer, nullptr)) {
    117      return;
    118    }
    119    mOuterDir = mOuterDirBuffer;
    120 
    121    if (FAILED(::StringCbPrintfW(mInnerDirBuffer, sizeof(mInnerDirBuffer),
    122                                 L"\\\\?\\%s%s\\%s", tempDir,
    123                                 kFolderName160Chars, kFolderName160Chars)) ||
    124        !::CreateDirectoryW(mInnerDirBuffer, nullptr)) {
    125      return;
    126    }
    127    mInnerDir = mInnerDirBuffer;
    128 
    129    if (FAILED(::StringCbPrintfW(mTargetFileBuffer, sizeof(mTargetFileBuffer),
    130                                 L"\\\\?\\%s%s\\%s\\%s", tempDir,
    131                                 kFolderName160Chars, kFolderName160Chars,
    132                                 aNewLeafNameAfterCopy)) ||
    133        !::CopyFileW(thisExe.get(), mTargetFileBuffer,
    134                     /*bFailIfExists*/ TRUE)) {
    135      return;
    136    }
    137    mTargetFile = mTargetFileBuffer;
    138  }
    139 
    140  ~LongNameModule() {
    141    if (mTargetFile) {
    142      ::DeleteFileW(mTargetFile);
    143    }
    144    if (mInnerDir) {
    145      ::RemoveDirectoryW(mInnerDir);
    146    }
    147    if (mOuterDir) {
    148      ::RemoveDirectoryW(mOuterDir);
    149    }
    150  }
    151 
    152  operator const wchar_t*() const { return mTargetFile; }
    153 };
    154 
    155 // Make sure module info retrieved from nt::PEHeaders is the same as one
    156 // retrieved from GetModuleInformation API.
    157 bool CompareModuleInfo(HMODULE aModuleForApi, HMODULE aModuleForPEHeader) {
    158  MODULEINFO moduleInfo;
    159  if (!::GetModuleInformation(::GetCurrentProcess(), aModuleForApi, &moduleInfo,
    160                              sizeof(moduleInfo))) {
    161    printf("TEST-FAILED | NativeNt | GetModuleInformation failed - %08lx\n",
    162           ::GetLastError());
    163    return false;
    164  }
    165 
    166  PEHeaders headers(aModuleForPEHeader);
    167  if (!headers) {
    168    printf("TEST-FAILED | NativeNt | Failed to instantiate PEHeaders\n");
    169    return false;
    170  }
    171 
    172  Maybe<Range<const uint8_t>> bounds = headers.GetBounds();
    173  if (!bounds) {
    174    printf("TEST-FAILED | NativeNt | PEHeaders::GetBounds failed\n");
    175    return false;
    176  }
    177 
    178  if (bounds->length() != moduleInfo.SizeOfImage) {
    179    printf("TEST-FAILED | NativeNt | SizeOfImage does not match\n");
    180    return false;
    181  }
    182 
    183  // GetModuleInformation sets EntryPoint to 0 for executables
    184  // except the running self.
    185  static const HMODULE sSelf = ::GetModuleHandleW(nullptr);
    186  if (aModuleForApi != sSelf &&
    187      !(headers.GetFileCharacteristics() & IMAGE_FILE_DLL)) {
    188    if (moduleInfo.EntryPoint) {
    189      printf(
    190          "TEST-FAIL | NativeNt | "
    191          "GetModuleInformation returned a non-zero entrypoint "
    192          "for an executable\n");
    193      return false;
    194    }
    195 
    196    // Cannot verify PEHeaders::GetEntryPoint.
    197    return true;
    198  }
    199 
    200  // For a module whose entrypoint is 0 (e.g. ntdll.dll or win32u.dll),
    201  // MODULEINFO::EntryPoint is set to 0, while PEHeaders::GetEntryPoint
    202  // returns the imagebase (RVA=0).
    203  intptr_t rvaEntryPoint =
    204      moduleInfo.EntryPoint
    205          ? reinterpret_cast<uintptr_t>(moduleInfo.EntryPoint) -
    206                reinterpret_cast<uintptr_t>(moduleInfo.lpBaseOfDll)
    207          : 0;
    208  if (rvaEntryPoint < 0) {
    209    printf("TEST-FAILED | NativeNt | MODULEINFO is invalid\n");
    210    return false;
    211  }
    212 
    213  if (headers.RVAToPtr<FARPROC>(rvaEntryPoint) != headers.GetEntryPoint()) {
    214    printf("TEST-FAILED | NativeNt | Entrypoint does not match\n");
    215    return false;
    216  }
    217 
    218  return true;
    219 }
    220 
    221 bool TestModuleInfo() {
    222  UNICODE_STRING newLeafName;
    223  ::RtlInitUnicodeString(&newLeafName,
    224                         L"\u672D\u5E4C\u5473\u564C.\u30E9\u30FC\u30E1\u30F3");
    225 
    226  LongNameModule longNameModule(newLeafName.Buffer);
    227  if (!longNameModule) {
    228    printf(
    229        "TEST-FAILED | NativeNt | "
    230        "Failed to copy the executable to a long directory path\n");
    231    return 1;
    232  }
    233 
    234  {
    235    nsModuleHandle module(::LoadLibraryW(longNameModule));
    236 
    237    bool detectedTarget = false;
    238    bool passedAllModules = true;
    239    auto moduleCallback = [&](const wchar_t* aModulePath, HMODULE aModule) {
    240      UNICODE_STRING modulePath, moduleName;
    241      ::RtlInitUnicodeString(&modulePath, aModulePath);
    242      GetLeafName(&moduleName, &modulePath);
    243      if (::RtlEqualUnicodeString(&moduleName, &newLeafName,
    244                                  /*aCaseInsensitive*/ TRUE)) {
    245        detectedTarget = true;
    246      }
    247 
    248      if (!CompareModuleInfo(aModule, aModule)) {
    249        passedAllModules = false;
    250      }
    251    };
    252 
    253    if (!mozilla::EnumerateProcessModules(moduleCallback)) {
    254      printf("TEST-FAILED | NativeNt | EnumerateProcessModules failed\n");
    255      return false;
    256    }
    257 
    258    if (!detectedTarget) {
    259      printf(
    260          "TEST-FAILED | NativeNt | "
    261          "EnumerateProcessModules missed the target file\n");
    262      return false;
    263    }
    264 
    265    if (!passedAllModules) {
    266      return false;
    267    }
    268  }
    269 
    270  return true;
    271 }
    272 
    273 // Make sure PEHeaders works for a module loaded with LOAD_LIBRARY_AS_DATAFILE
    274 // as well as a module loaded normally.
    275 bool TestModuleLoadedAsData() {
    276  const wchar_t kNewLeafName[] = L"\u03BC\u0061\u9EBA.txt";
    277 
    278  LongNameModule longNameModule(kNewLeafName);
    279  if (!longNameModule) {
    280    printf(
    281        "TEST-FAILED | NativeNt | "
    282        "Failed to copy the executable to a long directory path\n");
    283    return 1;
    284  }
    285 
    286  const wchar_t* kManualLoadModules[] = {
    287      L"mshtml.dll",
    288      L"shell32.dll",
    289      longNameModule,
    290  };
    291 
    292  for (const auto moduleName : kManualLoadModules) {
    293    // Must load a module as data first,
    294    nsModuleHandle moduleAsData(::LoadLibraryExW(
    295        moduleName, nullptr,
    296        LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE));
    297 
    298    // then load a module normally to map it on a different address.
    299    nsModuleHandle module(::LoadLibraryW(moduleName));
    300 
    301    if (!CompareModuleInfo(module.get(), moduleAsData.get())) {
    302      return false;
    303    }
    304 
    305    PEHeaders peAsData(moduleAsData.get());
    306    PEHeaders pe(module.get());
    307    if (!peAsData || !pe) {
    308      printf("TEST-FAIL | NativeNt | Failed to load the module\n");
    309      return false;
    310    }
    311 
    312    if (peAsData.RVAToPtr<HMODULE>(0) == pe.RVAToPtr<HMODULE>(0)) {
    313      printf(
    314          "TEST-FAIL | NativeNt | "
    315          "The module should have been mapped onto two different places\n");
    316      return false;
    317    }
    318 
    319    const auto* pdb1 = peAsData.GetPdbInfo();
    320    const auto* pdb2 = pe.GetPdbInfo();
    321    if (pdb1 && pdb2) {
    322      if (pdb1->pdbSignature != pdb2->pdbSignature ||
    323          pdb1->pdbAge != pdb2->pdbAge ||
    324          strcmp(pdb1->pdbFileName, pdb2->pdbFileName)) {
    325        printf(
    326            "TEST-FAIL | NativeNt | "
    327            "PDB info from the same module did not match.\n");
    328        return false;
    329      }
    330    } else if (pdb1 || pdb2) {
    331      printf(
    332          "TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n");
    333      return false;
    334    }
    335 
    336    uint64_t version1, version2;
    337    bool result1 = peAsData.GetVersionInfo(version1);
    338    bool result2 = pe.GetVersionInfo(version2);
    339    if (result1 && result2) {
    340      if (version1 != version2) {
    341        printf("TEST-FAIL | NativeNt | Version mismatch\n");
    342        return false;
    343      }
    344    } else if (result1 || result2) {
    345      printf(
    346          "TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n");
    347      return false;
    348    }
    349  }
    350 
    351  return true;
    352 }
    353 
    354 LauncherResult<HMODULE> GetModuleHandleFromLeafName(const wchar_t* aName) {
    355  UNICODE_STRING name;
    356  ::RtlInitUnicodeString(&name, aName);
    357  return nt::GetModuleHandleFromLeafName(name);
    358 }
    359 
    360 // Need a non-inline function to bypass compiler optimization that the thread
    361 // local storage pointer is cached in a register before accessing a thread-local
    362 // variable.
    363 MOZ_NEVER_INLINE PVOID SwapThreadLocalStoragePointer(PVOID aNewValue) {
    364  auto oldValue = RtlGetThreadLocalStoragePointer();
    365  RtlSetThreadLocalStoragePointerForTestingOnly(aNewValue);
    366  return oldValue;
    367 }
    368 
    369 #if defined(_M_X64)
    370 bool TestCheckStack() {
    371  auto stackBase = reinterpret_cast<uint8_t*>(RtlGetThreadStackBase());
    372  auto stackLimit = reinterpret_cast<uint8_t*>(RtlGetThreadStackLimit());
    373  uint8_t* stackPointer = nullptr;
    374  asm volatile("mov %%rsp, %0;" : "=r"(stackPointer));
    375  if (!(stackLimit < stackBase && stackLimit <= stackPointer &&
    376        stackPointer < stackBase)) {
    377    printf("TEST-FAIL | NativeNt | Stack addresses are not coherent.\n");
    378    return false;
    379  }
    380  uintptr_t committedBytes = stackPointer - stackLimit;
    381  const uint32_t maxExtraCommittedBytes = 0x10000;
    382  if ((committedBytes + maxExtraCommittedBytes) >
    383      std::numeric_limits<uint32_t>::max()) {
    384    printf(
    385        "TEST-FAIL | NativeNt | The stack limit is too high to perform the "
    386        "test.\n");
    387    return false;
    388  }
    389  for (uint32_t extraSize = 0; extraSize < maxExtraCommittedBytes;
    390       ++extraSize) {
    391    CheckStack(static_cast<uint32_t>(committedBytes) + extraSize);
    392    auto expectedNewLimit = stackLimit - ((extraSize + 0xFFF) & ~0xFFF);
    393    if (expectedNewLimit != RtlGetThreadStackLimit()) {
    394      printf(
    395          "TEST-FAIL | NativeNt | CheckStack did not grow the stack "
    396          "correctly (expected: %p, got: %p).\n",
    397          expectedNewLimit, RtlGetThreadStackLimit());
    398      return false;
    399    }
    400  }
    401  return true;
    402 }
    403 #endif  // _M_X64
    404 
    405 int wmain(int argc, wchar_t* argv[]) {
    406  UNICODE_STRING normal;
    407  ::RtlInitUnicodeString(&normal, kNormal);
    408 
    409  UNICODE_STRING hex12;
    410  ::RtlInitUnicodeString(&hex12, kHex12);
    411 
    412  UNICODE_STRING hex16;
    413  ::RtlInitUnicodeString(&hex16, kHex16);
    414 
    415  UNICODE_STRING hex24;
    416  ::RtlInitUnicodeString(&hex24, kHex24);
    417 
    418  UNICODE_STRING hex8;
    419  ::RtlInitUnicodeString(&hex8, kHex8);
    420 
    421  UNICODE_STRING nonHex12;
    422  ::RtlInitUnicodeString(&nonHex12, kNonHex12);
    423 
    424  UNICODE_STRING hex13;
    425  ::RtlInitUnicodeString(&hex13, kHex13);
    426 
    427  UNICODE_STRING hex11;
    428  ::RtlInitUnicodeString(&hex11, kHex11);
    429 
    430  UNICODE_STRING hex15;
    431  ::RtlInitUnicodeString(&hex15, kHex15);
    432 
    433  UNICODE_STRING hex17;
    434  ::RtlInitUnicodeString(&hex17, kHex17);
    435 
    436  UNICODE_STRING prefixedHex16;
    437  ::RtlInitUnicodeString(&prefixedHex16, kPrefixedHex16);
    438 
    439  EXPECT_FAIL(Contains12DigitHexString, normal);
    440  EXPECT_SUCCESS(Contains12DigitHexString, hex12);
    441  EXPECT_FAIL(Contains12DigitHexString, hex13);
    442  EXPECT_FAIL(Contains12DigitHexString, hex11);
    443  EXPECT_FAIL(Contains12DigitHexString, hex16);
    444  EXPECT_FAIL(Contains12DigitHexString, nonHex12);
    445 
    446  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, normal);
    447  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex12);
    448  EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex24);
    449  EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex16);
    450  EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex17);
    451  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex8);
    452  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex15);
    453  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, prefixedHex16);
    454 
    455  if (RtlGetProcessHeap() != ::GetProcessHeap()) {
    456    printf("TEST-FAILED | NativeNt | RtlGetProcessHeap() is broken\n");
    457    return 1;
    458  }
    459 
    460 #ifdef HAVE_SEH_EXCEPTIONS
    461  PVOID origTlsHead = nullptr;
    462  bool isExceptionThrown = false;
    463  // Touch sTlsData.get() several times to prevent the call to sTlsData.set()
    464  // from being optimized out in PGO build.
    465  printf("sTlsData#1 = %08x\n", getTlsData());
    466  MOZ_SEH_TRY {
    467    // Need to call SwapThreadLocalStoragePointer inside __try to make sure
    468    // accessing sTlsData is caught by SEH.  This is due to clang's design.
    469    // https://bugs.llvm.org/show_bug.cgi?id=44174.
    470    origTlsHead = SwapThreadLocalStoragePointer(nullptr);
    471    setTlsData(~kTlsDataValue);
    472  }
    473  MOZ_SEH_EXCEPT(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
    474                     ? EXCEPTION_EXECUTE_HANDLER
    475                     : EXCEPTION_CONTINUE_SEARCH) {
    476    isExceptionThrown = true;
    477  }
    478  SwapThreadLocalStoragePointer(origTlsHead);
    479  printf("sTlsData#2 = %08x\n", getTlsData());
    480  setTlsData(kTlsDataValue);
    481  printf("sTlsData#3 = %08x\n", getTlsData());
    482  if (!isExceptionThrown || getTlsData() != kTlsDataValue) {
    483    printf(
    484        "TEST-FAILED | NativeNt | RtlGetThreadLocalStoragePointer() is "
    485        "broken\n");
    486    return 1;
    487  }
    488 #endif
    489 
    490  if (RtlGetCurrentThreadId() != ::GetCurrentThreadId()) {
    491    printf("TEST-FAILED | NativeNt | RtlGetCurrentThreadId() is broken\n");
    492    return 1;
    493  }
    494 
    495  const wchar_t kKernel32[] = L"kernel32.dll";
    496  DWORD verInfoSize = ::GetFileVersionInfoSizeW(kKernel32, nullptr);
    497  if (!verInfoSize) {
    498    printf(
    499        "TEST-FAILED | NativeNt | Call to GetFileVersionInfoSizeW failed with "
    500        "code %lu\n",
    501        ::GetLastError());
    502    return 1;
    503  }
    504 
    505  auto verInfoBuf = MakeUnique<char[]>(verInfoSize);
    506 
    507  if (!::GetFileVersionInfoW(kKernel32, 0, verInfoSize, verInfoBuf.get())) {
    508    printf(
    509        "TEST-FAILED | NativeNt | Call to GetFileVersionInfoW failed with code "
    510        "%lu\n",
    511        ::GetLastError());
    512    return 1;
    513  }
    514 
    515  UINT len;
    516  VS_FIXEDFILEINFO* fixedFileInfo = nullptr;
    517  if (!::VerQueryValueW(verInfoBuf.get(), L"\\", (LPVOID*)&fixedFileInfo,
    518                        &len)) {
    519    printf(
    520        "TEST-FAILED | NativeNt | Call to VerQueryValueW failed with code "
    521        "%lu\n",
    522        ::GetLastError());
    523    return 1;
    524  }
    525 
    526  const uint64_t expectedVersion =
    527      (static_cast<uint64_t>(fixedFileInfo->dwFileVersionMS) << 32) |
    528      static_cast<uint64_t>(fixedFileInfo->dwFileVersionLS);
    529 
    530  PEHeaders k32headers(::GetModuleHandleW(kKernel32));
    531  if (!k32headers) {
    532    printf(
    533        "TEST-FAILED | NativeNt | Failed parsing kernel32.dll's PE headers\n");
    534    return 1;
    535  }
    536 
    537  uint64_t version;
    538  if (!k32headers.GetVersionInfo(version)) {
    539    printf(
    540        "TEST-FAILED | NativeNt | Unable to obtain version information from "
    541        "kernel32.dll\n");
    542    return 1;
    543  }
    544 
    545  if (version != expectedVersion) {
    546    printf(
    547        "TEST-FAILED | NativeNt | kernel32.dll's detected version "
    548        "(0x%016llX) does not match expected version (0x%016llX)\n",
    549        version, expectedVersion);
    550    return 1;
    551  }
    552 
    553  Maybe<Span<IMAGE_THUNK_DATA>> iatThunks =
    554      k32headers.GetIATThunksForModule("kernel32.dll");
    555  if (iatThunks) {
    556    printf(
    557        "TEST-FAILED | NativeNt | Detected the IAT thunk for kernel32 "
    558        "in kernel32.dll\n");
    559    return 1;
    560  }
    561 
    562  const mozilla::nt::CodeViewRecord70* debugInfo = k32headers.GetPdbInfo();
    563  if (!debugInfo) {
    564    printf(
    565        "TEST-FAILED | NativeNt | Unable to obtain debug information from "
    566        "kernel32.dll\n");
    567    return 1;
    568  }
    569 
    570 #ifndef WIN32  // failure on windows10x32
    571  if (stricmp(debugInfo->pdbFileName, "kernel32.pdb")) {
    572    printf(
    573        "TEST-FAILED | NativeNt | Unexpected PDB filename "
    574        "in kernel32.dll: %s\n",
    575        debugInfo->pdbFileName);
    576    return 1;
    577  }
    578 #endif
    579 
    580  PEHeaders ntdllheaders(::GetModuleHandleW(L"ntdll.dll"));
    581 
    582  auto ntdllBoundaries = ntdllheaders.GetBounds();
    583  if (!ntdllBoundaries) {
    584    printf(
    585        "TEST-FAILED | NativeNt | "
    586        "Unable to obtain the boundaries of ntdll.dll\n");
    587    return 1;
    588  }
    589 
    590  iatThunks =
    591      k32headers.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr());
    592  if (!iatThunks) {
    593    printf(
    594        "TEST-FAILED | NativeNt | Unable to find the IAT thunk for "
    595        "ntdll.dll in kernel32.dll\n");
    596    return 1;
    597  }
    598 
    599  // To test the Ex version of API, we purposely get a real handle
    600  // instead of a pseudo handle.
    601  nsAutoHandle process(
    602      ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()));
    603  if (!process) {
    604    printf("TEST-FAILED | NativeNt | OpenProcess() failed - %08lx\n",
    605           ::GetLastError());
    606    return 1;
    607  }
    608 
    609  // Test Null page, Heap, Mapped image, and Invalid handle
    610  if (!TestVirtualQuery(process, nullptr) || !TestVirtualQuery(process, argv) ||
    611      !TestVirtualQuery(process, kNormal) ||
    612      !TestVirtualQuery(nullptr, kNormal)) {
    613    return 1;
    614  }
    615 
    616  auto moduleResult = GetModuleHandleFromLeafName(kKernel32);
    617  if (moduleResult.isErr() ||
    618      moduleResult.inspect() != k32headers.template RVAToPtr<HMODULE>(0)) {
    619    printf(
    620        "TEST-FAILED | NativeNt | "
    621        "GetModuleHandleFromLeafName returns a wrong value.\n");
    622    return 1;
    623  }
    624 
    625  moduleResult = GetModuleHandleFromLeafName(L"invalid");
    626  if (moduleResult.isOk()) {
    627    printf(
    628        "TEST-FAILED | NativeNt | "
    629        "GetModuleHandleFromLeafName unexpectedly returns a value.\n");
    630    return 1;
    631  }
    632 
    633  if (!TestModuleInfo()) {
    634    return 1;
    635  }
    636 
    637  if (!TestModuleLoadedAsData()) {
    638    return 1;
    639  }
    640 
    641 #if defined(_M_X64)
    642  if (!TestCheckStack()) {
    643    return 1;
    644  }
    645 #endif  // _M_X64
    646 
    647  printf("TEST-PASS | NativeNt | All tests ran successfully\n");
    648  return 0;
    649 }