TestCrossProcessWin.cpp (23804B)
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 <thread> 8 #include <winternl.h> 9 10 #define MOZ_USE_LAUNCHER_ERROR 11 12 #include <atomic> 13 #include <thread> 14 #include "freestanding/SharedSection.cpp" 15 #include "mozilla/CmdLineAndEnvUtils.h" 16 #include "mozilla/DynamicBlocklist.h" 17 #include "mozilla/NativeNt.h" 18 #include "mozilla/Vector.h" 19 20 #define DLL_BLOCKLIST_ENTRY(name, ...) \ 21 {MOZ_LITERAL_UNICODE_STRING(L##name), __VA_ARGS__}, 22 #define DLL_BLOCKLIST_STRING_TYPE UNICODE_STRING 23 24 #include "mozilla/WindowsDllBlocklistLauncherDefs.h" 25 26 const wchar_t kChildArg[] = L"--child"; 27 const char* kTestDependentModulePaths[] = { 28 "\\Device\\HarddiskVolume4\\Windows\\system32\\A B C", 29 "\\Device\\HarddiskVolume4\\Windows\\system32\\a b c.dll", 30 "\\Device\\HarddiskVolume4\\Windows\\system32\\A B C.exe", 31 "\\Device\\HarddiskVolume4\\Windows\\system32\\X Y Z.dll", 32 "\\Device\\HarddiskVolume1\\a b C", 33 "\\Device\\HarddiskVolume2\\A b c.DLL", 34 "\\Device\\HarddiskVolume3\\A B c.exe", 35 "\\Device\\HarddiskVolume4\\X y Z.dll", 36 }; 37 const wchar_t* kExpectedDependentModules[] = { 38 L"A B C", 39 L"a b c.dll", 40 L"A B C.exe", 41 L"X Y Z.dll", 42 }; 43 44 const UNICODE_STRING kStringNotInBlocklist = 45 MOZ_LITERAL_UNICODE_STRING(L"Test_NotInBlocklist.dll"); 46 const UNICODE_STRING kTestDependentModuleString = 47 MOZ_LITERAL_UNICODE_STRING(L"Test_DependentModule.dll"); 48 49 using namespace mozilla; 50 using namespace mozilla::freestanding; 51 52 // clang-format off 53 const DllBlockInfo kDllBlocklistShort[] = { 54 // The entries do not have to be sorted. 55 DLL_BLOCKLIST_ENTRY("X Y Z_Test", MAKE_VERSION(1, 2, 65535, 65535)) 56 DLL_BLOCKLIST_ENTRY("\u30E9\u30FC\u30E1\u30F3_Test") 57 DLL_BLOCKLIST_ENTRY("Avmvirtualsource_Test.ax", MAKE_VERSION(1, 0, 0, 3), 58 DllBlockInfoFlags::BROWSER_PROCESS_ONLY) 59 DLL_BLOCKLIST_ENTRY("1ccelerator_Test.dll", MAKE_VERSION(3, 2, 1, 6)) 60 DLL_BLOCKLIST_ENTRY("atkdx11disp_Test.dll", DllBlockInfo::ALL_VERSIONS) 61 {}, 62 }; 63 // clang-format on 64 65 namespace mozilla::freestanding { 66 class SharedSectionTestHelper { 67 public: 68 static constexpr size_t GetModulePathArraySize() { 69 return SharedSection::kSharedViewSize - 70 (offsetof(SharedSection::Layout, mFirstBlockEntry) + 71 sizeof(DllBlockInfo)); 72 } 73 }; 74 } // namespace mozilla::freestanding 75 76 class TempFile final { 77 wchar_t mFullPath[MAX_PATH + 1]; 78 79 public: 80 TempFile() : mFullPath{0} { 81 wchar_t tempDir[MAX_PATH + 1]; 82 DWORD len = ::GetTempPathW(std::size(tempDir), tempDir); 83 if (!len) { 84 return; 85 } 86 87 len = ::GetTempFileNameW(tempDir, L"blocklist", 0, mFullPath); 88 if (!len) { 89 return; 90 } 91 } 92 93 operator const wchar_t*() const { return mFullPath[0] ? mFullPath : nullptr; } 94 }; 95 96 template <typename T, int N> 97 void PrintLauncherError(const LauncherResult<T>& aResult, 98 const char (&aMsg)[N]) { 99 const LauncherError& err = aResult.inspectErr(); 100 printf("TEST-FAILED | TestCrossProcessWin | %s - %lx at %s:%d\n", aMsg, 101 err.mError.AsHResult(), err.mFile, err.mLine); 102 } 103 104 #define VERIFY_FUNCTION_RESOLVED(mod, exports, name) \ 105 do { \ 106 if (reinterpret_cast<FARPROC>(exports->m##name) != \ 107 ::GetProcAddress(mod, #name)) { \ 108 printf( \ 109 "TEST-FAILED | TestCrossProcessWin | " \ 110 "Kernel32ExportsSolver::" #name " did not match.\n"); \ 111 return false; \ 112 } \ 113 } while (0) 114 115 static bool VerifySharedSection(SharedSection& aSharedSection) { 116 Kernel32ExportsSolver* k32Exports = aSharedSection.GetKernel32Exports(); 117 if (!k32Exports) { 118 printf( 119 "TEST-FAILED | TestCrossProcessWin | Failed to map a shared section\n"); 120 return false; 121 } 122 123 HMODULE k32mod = ::GetModuleHandleW(L"kernel32.dll"); 124 VERIFY_FUNCTION_RESOLVED(k32mod, k32Exports, FlushInstructionCache); 125 VERIFY_FUNCTION_RESOLVED(k32mod, k32Exports, GetModuleHandleW); 126 VERIFY_FUNCTION_RESOLVED(k32mod, k32Exports, GetSystemInfo); 127 VERIFY_FUNCTION_RESOLVED(k32mod, k32Exports, VirtualProtect); 128 129 Maybe<Vector<const wchar_t*>> modulesArray = 130 aSharedSection.GetDependentModules(); 131 if (modulesArray.isNothing()) { 132 printf( 133 "TEST-FAILED | TestCrossProcessWin | GetDependentModules returned " 134 "Nothing"); 135 return false; 136 } 137 bool matched = 138 modulesArray->length() == 139 sizeof(kExpectedDependentModules) / sizeof(kExpectedDependentModules[0]); 140 if (matched) { 141 for (size_t i = 0; i < modulesArray->length(); ++i) { 142 if (wcscmp((*modulesArray)[i], kExpectedDependentModules[i])) { 143 matched = false; 144 break; 145 } 146 } 147 } 148 if (!matched) { 149 // Print actual strings on error 150 for (const wchar_t* p : *modulesArray) { 151 printf("%p: %S\n", p, p); 152 } 153 return false; 154 } 155 156 for (const DllBlockInfo* info = kDllBlocklistShort; info->mName.Buffer; 157 ++info) { 158 const DllBlockInfo* matched = aSharedSection.SearchBlocklist(info->mName); 159 if (!matched) { 160 printf( 161 "TEST-FAILED | TestCrossProcessWin | No blocklist entry match for " 162 "entry in blocklist.\n"); 163 return false; 164 } 165 } 166 167 if (aSharedSection.SearchBlocklist(kStringNotInBlocklist)) { 168 printf( 169 "TEST-FAILED | TestCrossProcessWin | Found blocklist entry match for " 170 "something not in the blocklist.\n"); 171 } 172 173 if (aSharedSection.IsDisabled()) { 174 printf("TEST-FAILED | TestCrossProcessWin | Wrong disabled value.\n"); 175 } 176 177 return true; 178 } 179 180 static bool TestAddString() { 181 wchar_t testBuffer[3] = {0}; 182 UNICODE_STRING ustr; 183 184 // This makes |testBuffer| full. 185 ::RtlInitUnicodeString(&ustr, L"a"); 186 if (!AddString(testBuffer, ustr)) { 187 printf( 188 "TEST-FAILED | TestCrossProcessWin | " 189 "AddString failed.\n"); 190 return false; 191 } 192 193 // Adding a string to a full buffer should fail. 194 ::RtlInitUnicodeString(&ustr, L"b"); 195 if (AddString(testBuffer, ustr)) { 196 printf( 197 "TEST-FAILED | TestCrossProcessWin | " 198 "AddString caused OOB memory access.\n"); 199 return false; 200 } 201 202 bool matched = memcmp(testBuffer, L"a\0", sizeof(testBuffer)) == 0; 203 if (!matched) { 204 printf( 205 "TEST-FAILED | TestCrossProcessWin | " 206 "AddString wrote wrong values.\n"); 207 return false; 208 } 209 210 return true; 211 } 212 213 // Convert |aBlockEntries|, which is an array ending with an empty instance 214 // of DllBlockInfo, to DynamicBlockList by storing it to a temp file, loading 215 // as DynamicBlockList, and deleting the temp file. 216 static DynamicBlockList ConvertStaticBlocklistToDynamic( 217 const DllBlockInfo aBlockEntries[]) { 218 size_t originalLength = 0; 219 CheckedUint32 totalStringLen = 0; 220 for (const DllBlockInfo* entry = aBlockEntries; entry->mName.Length; 221 ++entry) { 222 totalStringLen += entry->mName.Length; 223 MOZ_RELEASE_ASSERT(totalStringLen.isValid()); 224 ++originalLength; 225 } 226 227 // Pack all strings in this buffer without null characters 228 UniquePtr<uint8_t[]> stringBuffer = 229 MakeUnique<uint8_t[]>(totalStringLen.value()); 230 231 // The string buffer is placed immediately after the array of DllBlockInfo 232 const size_t stringBufferOffset = (originalLength + 1) * sizeof(DllBlockInfo); 233 234 // Entries in the dynamic blocklist do have to be sorted, 235 // unlike in the static blocklist. 236 UniquePtr<DllBlockInfo[]> sortedBlockEntries = 237 MakeUnique<DllBlockInfo[]>(originalLength); 238 memcpy(sortedBlockEntries.get(), aBlockEntries, 239 sizeof(DllBlockInfo) * originalLength); 240 std::sort(sortedBlockEntries.get(), sortedBlockEntries.get() + originalLength, 241 [](const DllBlockInfo& a, const DllBlockInfo& b) { 242 return ::RtlCompareUnicodeString(&a.mName, &b.mName, TRUE) < 0; 243 }); 244 245 Vector<DllBlockInfo> copied; 246 (void)copied.resize(originalLength + 1); // aBlockEntries + sentinel 247 248 size_t currentStringOffset = 0; 249 for (size_t i = 0; i < originalLength; ++i) { 250 copied[i].mMaxVersion = sortedBlockEntries[i].mMaxVersion; 251 copied[i].mFlags = sortedBlockEntries[i].mFlags; 252 253 // Copy the module's name to the string buffer and store its offset 254 // in mName.Buffer 255 memcpy(stringBuffer.get() + currentStringOffset, 256 sortedBlockEntries[i].mName.Buffer, 257 sortedBlockEntries[i].mName.Length); 258 copied[i].mName.Buffer = 259 reinterpret_cast<wchar_t*>(stringBufferOffset + currentStringOffset); 260 // Only keep mName.Length and leave mName.MaximumLength to be zero 261 copied[i].mName.Length = sortedBlockEntries[i].mName.Length; 262 263 currentStringOffset += sortedBlockEntries[i].mName.Length; 264 } 265 266 TempFile blocklistFile; 267 nsAutoHandle file(::CreateFileW(blocklistFile, GENERIC_WRITE, FILE_SHARE_READ, 268 nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 269 nullptr)); 270 MOZ_RELEASE_ASSERT(file); 271 272 DynamicBlockListBase::FileHeader header; 273 header.mSignature = DynamicBlockListBase::kSignature; 274 header.mFileVersion = DynamicBlockListBase::kCurrentVersion; 275 header.mPayloadSize = 276 sizeof(DllBlockInfo) * copied.length() + totalStringLen.value(); 277 278 DWORD written = 0; 279 MOZ_RELEASE_ASSERT( 280 ::WriteFile(file.get(), &header, sizeof(header), &written, nullptr)); 281 MOZ_RELEASE_ASSERT(::WriteFile(file.get(), copied.begin(), 282 sizeof(DllBlockInfo) * copied.length(), 283 &written, nullptr)); 284 MOZ_RELEASE_ASSERT(::WriteFile(file.get(), stringBuffer.get(), 285 totalStringLen.value(), &written, nullptr)); 286 287 DynamicBlockList blockList(blocklistFile); 288 ::DeleteFileW(blocklistFile); 289 return blockList; 290 } 291 292 MOZ_RUNINIT const DynamicBlockList gFullList = 293 ConvertStaticBlocklistToDynamic(gWindowsDllBlocklist); 294 MOZ_RUNINIT const DynamicBlockList gShortList = 295 ConvertStaticBlocklistToDynamic(kDllBlocklistShort); 296 297 static bool TestDependentModules() { 298 LauncherVoidResult result = gSharedSection.Init(); 299 if (result.isErr()) { 300 PrintLauncherError(result, "SharedSection::Init failed"); 301 return false; 302 } 303 304 constexpr size_t sizeInBytes = 305 SharedSectionTestHelper::GetModulePathArraySize(); 306 UniquePtr<uint8_t[]> bufferData = MakeUnique<uint8_t[]>(sizeInBytes); 307 Span<uint8_t> buffer(bufferData, sizeInBytes); 308 memset(buffer.data(), 0x88, buffer.size()); 309 310 // Try to add a long string that does not fit in the section, 311 // since there's no room for the NULL character to indicate the final string. 312 UNICODE_STRING ustr; 313 ustr.Buffer = reinterpret_cast<wchar_t*>(buffer.data()); 314 ustr.Length = ustr.MaximumLength = buffer.size(); 315 316 result = gSharedSection.AddDependentModule(&ustr); 317 if (result.isOk() || result.inspectErr() != WindowsError::FromWin32Error( 318 ERROR_INSUFFICIENT_BUFFER)) { 319 printf( 320 "TEST-FAILED | TestCrossProcessWin | " 321 "Adding a too long string should fail.\n"); 322 return false; 323 } 324 325 result = gSharedSection.Init(); 326 if (result.isErr()) { 327 PrintLauncherError(result, "SharedSection::Init failed"); 328 return false; 329 } 330 331 // Keep adding a single-char string until it fails and 332 // make sure no crash. 333 // We want to make sure no strings match any earlier strings so 334 // we can get the expected count. This is a little tricky since 335 // it includes case-insensitivity, so start at the "CJK Unified Ideographs 336 // Extension A" block of Unicode, which has no two characters that compare 337 // equal under a case insensitive comparison. 338 *(reinterpret_cast<wchar_t*>(buffer.data())) = 0x3400; 339 ustr.Length = ustr.MaximumLength = sizeof(wchar_t); 340 wchar_t numberOfStringsAdded = 0; 341 while (gSharedSection.AddDependentModule(&ustr).isOk()) { 342 ++numberOfStringsAdded; 343 // Make sure the string doesn't match any earlier strings 344 wchar_t oldValue = *(reinterpret_cast<wchar_t*>(buffer.data())); 345 *(reinterpret_cast<wchar_t*>(buffer.data())) = oldValue + 1; 346 } 347 348 int numberOfCharactersInBuffer = 349 SharedSectionTestHelper::GetModulePathArraySize() / sizeof(wchar_t); 350 // Each string is two characters long (one "real" character and a null), but 351 // the whole buffer needs an additional null at the end. 352 int expectedNumberOfStringsAdded = (numberOfCharactersInBuffer - 1) / 2; 353 if (numberOfStringsAdded != expectedNumberOfStringsAdded) { 354 printf( 355 "TEST-FAILED | TestCrossProcessWin | " 356 "Added %d dependent strings before failing (expected %d).\n", 357 static_cast<int>(numberOfStringsAdded), expectedNumberOfStringsAdded); 358 return false; 359 } 360 361 // SetBlocklist is not allowed after AddDependentModule 362 result = gSharedSection.SetBlocklist(gShortList, false); 363 if (result.isOk() || result.inspectErr() != 364 WindowsError::FromWin32Error(ERROR_INVALID_STATE)) { 365 printf( 366 "TEST-FAILED | TestCrossProcessWin | " 367 "SetBlocklist is not allowed after AddDependentModule\n"); 368 return false; 369 } 370 371 gSharedSection.Reset(); 372 return true; 373 } 374 375 static bool TestDynamicBlocklist() { 376 if (!gFullList.GetPayloadSize() || !gShortList.GetPayloadSize()) { 377 printf( 378 "TEST-FAILED | TestCrossProcessWin | DynamicBlockList::LoadFile " 379 "failed\n"); 380 return false; 381 } 382 383 LauncherVoidResult result = gSharedSection.Init(); 384 if (result.isErr()) { 385 PrintLauncherError(result, "SharedSection::Init failed"); 386 return false; 387 } 388 389 // Set gShortList, and gShortList 390 // 1. Setting gShortList succeeds 391 // 2. Next try to set gShortList fails 392 result = gSharedSection.SetBlocklist(gShortList, false); 393 if (result.isErr()) { 394 PrintLauncherError(result, "SetBlocklist(gShortList) failed"); 395 return false; 396 } 397 result = gSharedSection.SetBlocklist(gShortList, false); 398 if (result.isOk() || result.inspectErr() != 399 WindowsError::FromWin32Error(ERROR_INVALID_STATE)) { 400 printf( 401 "TEST-FAILED | TestCrossProcessWin | " 402 "SetBlocklist is allowed only once\n"); 403 return false; 404 } 405 406 result = gSharedSection.Init(); 407 if (result.isErr()) { 408 PrintLauncherError(result, "SharedSection::Init failed"); 409 return false; 410 } 411 412 // Add gFullList and gShortList 413 // 1. Adding gFullList always fails because it doesn't fit the section 414 // 2. Adding gShortList succeeds because no entry is added yet 415 MOZ_RELEASE_ASSERT( 416 gFullList.GetPayloadSize() > 417 SharedSectionTestHelper::GetModulePathArraySize(), 418 "Test assumes gFullList is too big to fit in shared section"); 419 result = gSharedSection.SetBlocklist(gFullList, false); 420 if (result.isOk() || result.inspectErr() != WindowsError::FromWin32Error( 421 ERROR_INSUFFICIENT_BUFFER)) { 422 printf( 423 "TEST-FAILED | TestCrossProcessWin | " 424 "SetBlocklist(gFullList) should fail\n"); 425 return false; 426 } 427 result = gSharedSection.SetBlocklist(gShortList, false); 428 if (result.isErr()) { 429 PrintLauncherError(result, "SetBlocklist(gShortList) failed"); 430 return false; 431 } 432 433 // AddDependentModule is allowed after SetBlocklist 434 result = gSharedSection.AddDependentModule(&kTestDependentModuleString); 435 if (result.isErr()) { 436 PrintLauncherError(result, "SharedSection::AddDependentModule failed"); 437 return false; 438 } 439 440 gSharedSection.Reset(); 441 return true; 442 } 443 444 class ChildProcess final { 445 nsAutoHandle mChildProcess; 446 nsAutoHandle mChildMainThread; 447 DWORD mProcessId; 448 449 public: 450 // The following variables are updated from the parent process via 451 // WriteProcessMemory while the process is suspended as a part of 452 // TestWithChildProcess(). 453 // 454 // Having both a non-const and a const is important because a constant 455 // is separately placed in the .rdata section which is read-only, so 456 // the region's attribute needs to be changed before modifying data via 457 // WriteProcessMemory. 458 // The keyword "volatile" is needed for a constant, otherwise the compiler 459 // evaluates a constant as a literal without fetching data from memory. 460 static HMODULE sExecutableImageBase; 461 static volatile const DWORD sReadOnlyProcessId; 462 463 static int Main() { 464 SRWLOCK lock = SRWLOCK_INIT; 465 ::AcquireSRWLockExclusive(&lock); 466 467 Vector<std::thread> threads; 468 std::atomic<bool> success = true; 469 for (int i = 0; i < 10; ++i) { 470 (void)threads.emplaceBack( 471 [&success](SRWLOCK* aLock) { 472 // All threads call GetKernel32Exports(), but only the first thread 473 // maps a write-copy section and populates it. 474 ::AcquireSRWLockShared(aLock); 475 if (gSharedSection.GetKernel32Exports() == nullptr) { 476 success = false; 477 } 478 ::ReleaseSRWLockShared(aLock); 479 }, 480 &lock); 481 } 482 483 // Wait a msec for all threads to be ready and release the lock 484 ::Sleep(1); 485 ::ReleaseSRWLockExclusive(&lock); 486 487 for (auto& thread : threads) { 488 thread.join(); 489 } 490 491 if (!success) { 492 printf( 493 "TEST-FAILED | TestCrossProcessWin | " 494 "GetKernel32Exports() returned null.\n"); 495 return 1; 496 } 497 498 if (sExecutableImageBase != ::GetModuleHandle(nullptr)) { 499 printf( 500 "TEST-FAILED | TestCrossProcessWin | " 501 "sExecutableImageBase is expected to be %p, but actually was %p.\n", 502 ::GetModuleHandle(nullptr), sExecutableImageBase); 503 return 1; 504 } 505 506 if (sReadOnlyProcessId != ::GetCurrentProcessId()) { 507 printf( 508 "TEST-FAILED | TestCrossProcessWin | " 509 "sReadOnlyProcessId is expected to be %lx, but actually was %lx.\n", 510 ::GetCurrentProcessId(), sReadOnlyProcessId); 511 return 1; 512 } 513 514 if (!VerifySharedSection(gSharedSection)) { 515 return 1; 516 } 517 518 // Test a scenario to transfer a transferred section as a readonly handle 519 gSharedSection.ConvertToReadOnly(); 520 521 // AddDependentModule fails as the handle is readonly. 522 LauncherVoidResult result = 523 gSharedSection.AddDependentModule(&kTestDependentModuleString); 524 if (result.inspectErr() != 525 WindowsError::FromWin32Error(ERROR_ACCESS_DENIED)) { 526 PrintLauncherError(result, "The readonly section was writable"); 527 return 1; 528 } 529 530 if (!VerifySharedSection(gSharedSection)) { 531 return 1; 532 } 533 534 return 0; 535 } 536 537 ChildProcess(const wchar_t* aExecutable, const wchar_t* aOption) 538 : mProcessId(0) { 539 const wchar_t* childArgv[] = {aExecutable, aOption}; 540 auto cmdLine(mozilla::MakeCommandLine(std::size(childArgv), childArgv)); 541 542 STARTUPINFOW si = {sizeof(si)}; 543 PROCESS_INFORMATION pi; 544 BOOL ok = 545 ::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr, FALSE, 546 CREATE_SUSPENDED, nullptr, nullptr, &si, &pi); 547 if (!ok) { 548 printf( 549 "TEST-FAILED | TestCrossProcessWin | " 550 "CreateProcessW falied - %08lx.\n", 551 GetLastError()); 552 return; 553 } 554 555 mProcessId = pi.dwProcessId; 556 557 mChildProcess.own(pi.hProcess); 558 mChildMainThread.own(pi.hThread); 559 } 560 561 ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); } 562 563 operator HANDLE() const { return mChildProcess; } 564 DWORD GetProcessId() const { return mProcessId; } 565 566 bool ResumeAndWaitUntilExit() { 567 if (::ResumeThread(mChildMainThread) == 0xffffffff) { 568 printf( 569 "TEST-FAILED | TestCrossProcessWin | " 570 "ResumeThread failed - %08lx\n", 571 GetLastError()); 572 return false; 573 } 574 575 if (::WaitForSingleObject(mChildProcess, 60000) != WAIT_OBJECT_0) { 576 printf( 577 "TEST-FAILED | TestCrossProcessWin | " 578 "Unexpected result from WaitForSingleObject\n"); 579 return false; 580 } 581 582 DWORD exitCode; 583 if (!::GetExitCodeProcess(mChildProcess, &exitCode)) { 584 printf( 585 "TEST-FAILED | TestCrossProcessWin | " 586 "GetExitCodeProcess failed - %08lx\n", 587 GetLastError()); 588 return false; 589 } 590 591 return exitCode == 0; 592 } 593 }; 594 595 HMODULE ChildProcess::sExecutableImageBase = 0; 596 volatile const DWORD ChildProcess::sReadOnlyProcessId = 0; 597 598 int wmain(int argc, wchar_t* argv[]) { 599 printf("Process: %-8lx Base: %p\n", ::GetCurrentProcessId(), 600 ::GetModuleHandle(nullptr)); 601 602 if (argc == 2 && wcscmp(argv[1], kChildArg) == 0) { 603 return ChildProcess::Main(); 604 } 605 606 ChildProcess childProcess(argv[0], kChildArg); 607 if (!childProcess) { 608 return 1; 609 } 610 611 if (!TestAddString()) { 612 return 1; 613 } 614 615 if (!TestDependentModules()) { 616 return 1; 617 } 618 619 if (!TestDynamicBlocklist()) { 620 return 1; 621 } 622 623 LauncherResult<HMODULE> remoteImageBase = 624 nt::GetProcessExeModule(childProcess); 625 if (remoteImageBase.isErr()) { 626 PrintLauncherError(remoteImageBase, "nt::GetProcessExeModule failed"); 627 return 1; 628 } 629 630 nt::CrossExecTransferManager transferMgr(childProcess); 631 if (!transferMgr) { 632 printf( 633 "TEST-FAILED | TestCrossProcessWin | " 634 "CrossExecTransferManager instantiation failed.\n"); 635 return 1; 636 } 637 638 LauncherVoidResult result = 639 transferMgr.Transfer(&ChildProcess::sExecutableImageBase, 640 &remoteImageBase.inspect(), sizeof(HMODULE)); 641 if (result.isErr()) { 642 PrintLauncherError(result, "ChildProcess::WriteData(Imagebase) failed"); 643 return 1; 644 } 645 646 DWORD childPid = childProcess.GetProcessId(); 647 648 DWORD* readOnlyData = const_cast<DWORD*>(&ChildProcess::sReadOnlyProcessId); 649 result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD)); 650 if (result.isOk()) { 651 printf( 652 "TEST-UNEXPECTED | TestCrossProcessWin | " 653 "A constant was located in a writable section."); 654 return 1; 655 } 656 657 AutoVirtualProtect prot = 658 transferMgr.Protect(readOnlyData, sizeof(uint32_t), PAGE_READWRITE); 659 if (!prot) { 660 printf( 661 "TEST-FAILED | TestCrossProcessWin | " 662 "VirtualProtect failed - %08lx\n", 663 prot.GetError().AsHResult()); 664 return 1; 665 } 666 667 result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD)); 668 if (result.isErr()) { 669 PrintLauncherError(result, "ChildProcess::WriteData(PID) failed"); 670 return 1; 671 } 672 673 result = gSharedSection.Init(); 674 if (result.isErr()) { 675 PrintLauncherError(result, "SharedSection::Init failed"); 676 return 1; 677 } 678 679 result = gSharedSection.SetBlocklist(gShortList, false); 680 if (result.isErr()) { 681 PrintLauncherError(result, "SetBlocklist(gShortList) failed"); 682 return false; 683 } 684 685 for (const char* testString : kTestDependentModulePaths) { 686 // Test AllocatedUnicodeString(const char*) that is used 687 // in IsDependentModule() 688 nt::AllocatedUnicodeString depModule(testString); 689 UNICODE_STRING depModuleLeafName; 690 nt::GetLeafName(&depModuleLeafName, depModule); 691 result = gSharedSection.AddDependentModule(&depModuleLeafName); 692 if (result.isErr()) { 693 PrintLauncherError(result, "SharedSection::AddDependentModule failed"); 694 return 1; 695 } 696 } 697 698 result = 699 gSharedSection.TransferHandle(transferMgr, GENERIC_READ | GENERIC_WRITE); 700 if (result.isErr()) { 701 PrintLauncherError(result, "SharedSection::TransferHandle failed"); 702 return 1; 703 } 704 705 // Close the section in the parent process before resuming the child process 706 gSharedSection.Reset(nullptr); 707 708 if (!childProcess.ResumeAndWaitUntilExit()) { 709 return 1; 710 } 711 712 printf("TEST-PASS | TestCrossProcessWin | All checks passed\n"); 713 return 0; 714 }