forked from mirrors/gecko-dev
		
	browser/app/winlauncher/test/TestCrossProcessWin.cpp(151,26): error: 'const' type qualifier on return type has no effect [-Werror,-Wignored-qualifiers]
        reinterpret_cast<const wchar_t (*)()>(::GetProcAddress(
                         ^~~~~~
widget/windows/nsWindow.h(374,8): error: 'const' type qualifier on return type has no effect [-Werror,-Wignored-qualifiers]
  bool const DestroyCalled() { return mDestroyCalled; }
       ^~~~~~
widget/windows/nsFilePicker.h(93,5): error: 'const' type qualifier on return type has no effect [-Werror,-Wignored-qualifiers]
    const uint32_t Length() { return mSpecList.Length(); }
    ^~~~~~
widget/windows/nsFilePicker.h(95,5): error: 'const' type qualifier on return type has no effect [-Werror,-Wignored-qualifiers]
    const bool IsEmpty() { return (mSpecList.Length() == 0); }
    ^~~~~~
Differential Revision: https://phabricator.services.mozilla.com/D144663
		
	
			
		
			
				
	
	
		
			365 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#define MOZ_USE_LAUNCHER_ERROR
 | 
						|
 | 
						|
#include "freestanding/SharedSection.cpp"
 | 
						|
#include "mozilla/CmdLineAndEnvUtils.h"
 | 
						|
#include "mozilla/NativeNt.h"
 | 
						|
 | 
						|
const wchar_t kChildArg[] = L"--child";
 | 
						|
const char kTestStrings[][6] = {
 | 
						|
    "a b c", "A B C", "a b c", "A B C", "X Y Z",
 | 
						|
};
 | 
						|
const wchar_t kTestStringsMerged[] =
 | 
						|
    L"a b c"
 | 
						|
    L"\0"
 | 
						|
    L"X Y Z"
 | 
						|
    L"\0";
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::freestanding;
 | 
						|
 | 
						|
template <typename T, int N>
 | 
						|
void PrintLauncherError(const LauncherResult<T>& aResult,
 | 
						|
                        const char (&aMsg)[N]) {
 | 
						|
  const LauncherError& err = aResult.inspectErr();
 | 
						|
  printf("TEST-FAILED | TestCrossProcessWin | %s - %lx at %s:%d\n", aMsg,
 | 
						|
         err.mError.AsHResult(), err.mFile, err.mLine);
 | 
						|
}
 | 
						|
 | 
						|
#define VERIFY_FUNCTION_RESOLVED(mod, exports, name)            \
 | 
						|
  do {                                                          \
 | 
						|
    if (reinterpret_cast<FARPROC>(exports.m##name) !=           \
 | 
						|
        ::GetProcAddress(mod, #name)) {                         \
 | 
						|
      printf(                                                   \
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "                \
 | 
						|
          "Kernel32ExportsSolver::" #name " did not match.\n"); \
 | 
						|
      return false;                                             \
 | 
						|
    }                                                           \
 | 
						|
  } while (0)
 | 
						|
 | 
						|
static bool VerifySharedSection(SharedSection& aSharedSection) {
 | 
						|
  LauncherResult<SharedSection::Layout*> resultView = aSharedSection.GetView();
 | 
						|
  if (resultView.isErr()) {
 | 
						|
    PrintLauncherError(resultView, "Failed to map a shared section");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  SharedSection::Layout* view = resultView.unwrap();
 | 
						|
 | 
						|
  // Use a local variable of RTL_RUN_ONCE to resolve Kernel32Exports every time
 | 
						|
  RTL_RUN_ONCE sRunEveryTime = RTL_RUN_ONCE_INIT;
 | 
						|
  view->mK32Exports.Resolve(sRunEveryTime);
 | 
						|
 | 
						|
  HMODULE k32mod = ::GetModuleHandleW(L"kernel32.dll");
 | 
						|
  VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, FlushInstructionCache);
 | 
						|
  VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetModuleHandleW);
 | 
						|
  VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, GetSystemInfo);
 | 
						|
  VERIFY_FUNCTION_RESOLVED(k32mod, view->mK32Exports, VirtualProtect);
 | 
						|
 | 
						|
  bool matched = memcmp(view->mModulePathArray, kTestStringsMerged,
 | 
						|
                        sizeof(kTestStringsMerged)) == 0;
 | 
						|
  if (!matched) {
 | 
						|
    // Print actual strings on error
 | 
						|
    for (const wchar_t* p = view->mModulePathArray; *p;) {
 | 
						|
      printf("%p: %ls\n", p, p);
 | 
						|
      while (*p) {
 | 
						|
        ++p;
 | 
						|
      }
 | 
						|
      ++p;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
static bool TestAddString() {
 | 
						|
  wchar_t testBuffer[3] = {0};
 | 
						|
  UNICODE_STRING ustr;
 | 
						|
 | 
						|
  // This makes |testBuffer| full.
 | 
						|
  ::RtlInitUnicodeString(&ustr, L"a");
 | 
						|
  if (!AddString(testBuffer, sizeof(testBuffer), ustr)) {
 | 
						|
    printf(
 | 
						|
        "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
        "AddString failed.\n");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // Adding a string to a full buffer should fail.
 | 
						|
  ::RtlInitUnicodeString(&ustr, L"b");
 | 
						|
  if (AddString(testBuffer, sizeof(testBuffer), ustr)) {
 | 
						|
    printf(
 | 
						|
        "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
        "AddString caused OOB memory access.\n");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  bool matched = memcmp(testBuffer, L"a\0", sizeof(testBuffer)) == 0;
 | 
						|
  if (!matched) {
 | 
						|
    printf(
 | 
						|
        "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
        "AddString wrote wrong values.\n");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
class ChildProcess final {
 | 
						|
  nsAutoHandle mChildProcess;
 | 
						|
  nsAutoHandle mChildMainThread;
 | 
						|
  DWORD mProcessId;
 | 
						|
 | 
						|
 public:
 | 
						|
  // The following variables are updated from the parent process via
 | 
						|
  // WriteProcessMemory while the process is suspended as a part of
 | 
						|
  // TestWithChildProcess().
 | 
						|
  //
 | 
						|
  // Having both a non-const and a const is important because a constant
 | 
						|
  // is separately placed in the .rdata section which is read-only, so
 | 
						|
  // the region's attribute needs to be changed before modifying data via
 | 
						|
  // WriteProcessMemory.
 | 
						|
  // The keyword "volatile" is needed for a constant, otherwise the compiler
 | 
						|
  // evaluates a constant as a literal without fetching data from memory.
 | 
						|
  static HMODULE sExecutableImageBase;
 | 
						|
  static volatile const DWORD sReadOnlyProcessId;
 | 
						|
 | 
						|
  static int Main() {
 | 
						|
    if (sExecutableImageBase != ::GetModuleHandle(nullptr)) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "sExecutableImageBase is expected to be %p, but actually was %p.\n",
 | 
						|
          ::GetModuleHandle(nullptr), sExecutableImageBase);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (sReadOnlyProcessId != ::GetCurrentProcessId()) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "sReadOnlyProcessId is expected to be %lx, but actually was %lx.\n",
 | 
						|
          ::GetCurrentProcessId(), sReadOnlyProcessId);
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    auto getDependentModulePaths =
 | 
						|
        reinterpret_cast<const wchar_t* (*)()>(::GetProcAddress(
 | 
						|
            ::GetModuleHandleW(nullptr), "GetDependentModulePaths"));
 | 
						|
    if (!getDependentModulePaths) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "Failed to get a pointer to GetDependentModulePaths - %08lx.\n",
 | 
						|
          ::GetLastError());
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
#if !defined(DEBUG)
 | 
						|
    // GetDependentModulePaths does not allow a caller other than xul.dll.
 | 
						|
    // Skip on Debug build because it hits MOZ_ASSERT.
 | 
						|
    if (getDependentModulePaths()) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "GetDependentModulePaths should return zero if the caller is "
 | 
						|
          "not xul.dll.\n");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
#endif  // !defined(DEBUG)
 | 
						|
 | 
						|
    if (!VerifySharedSection(gSharedSection)) {
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    // Test a scenario to transfer a transferred section as a readonly handle
 | 
						|
    static HANDLE copiedHandle = nullptr;
 | 
						|
    nt::CrossExecTransferManager tansferToSelf(::GetCurrentProcess());
 | 
						|
    LauncherVoidResult result = gSharedSection.TransferHandle(
 | 
						|
        tansferToSelf, GENERIC_READ, &copiedHandle);
 | 
						|
    if (result.isErr()) {
 | 
						|
      PrintLauncherError(result, "SharedSection::TransferHandle(self) failed");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    gSharedSection.Reset(copiedHandle);
 | 
						|
 | 
						|
    UNICODE_STRING ustr;
 | 
						|
    ::RtlInitUnicodeString(&ustr, L"test");
 | 
						|
    result = gSharedSection.AddDepenentModule(&ustr);
 | 
						|
    if (result.inspectErr() !=
 | 
						|
        WindowsError::FromWin32Error(ERROR_ACCESS_DENIED)) {
 | 
						|
      PrintLauncherError(result, "The readonly section was writable");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!VerifySharedSection(gSharedSection)) {
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  ChildProcess(const wchar_t* aExecutable, const wchar_t* aOption)
 | 
						|
      : mProcessId(0) {
 | 
						|
    const wchar_t* childArgv[] = {aExecutable, aOption};
 | 
						|
    auto cmdLine(
 | 
						|
        mozilla::MakeCommandLine(mozilla::ArrayLength(childArgv), childArgv));
 | 
						|
 | 
						|
    STARTUPINFOW si = {sizeof(si)};
 | 
						|
    PROCESS_INFORMATION pi;
 | 
						|
    BOOL ok =
 | 
						|
        ::CreateProcessW(aExecutable, cmdLine.get(), nullptr, nullptr, FALSE,
 | 
						|
                         CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);
 | 
						|
    if (!ok) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "CreateProcessW falied - %08lx.\n",
 | 
						|
          GetLastError());
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    mProcessId = pi.dwProcessId;
 | 
						|
 | 
						|
    mChildProcess.own(pi.hProcess);
 | 
						|
    mChildMainThread.own(pi.hThread);
 | 
						|
  }
 | 
						|
 | 
						|
  ~ChildProcess() { ::TerminateProcess(mChildProcess, 0); }
 | 
						|
 | 
						|
  operator HANDLE() const { return mChildProcess; }
 | 
						|
  DWORD GetProcessId() const { return mProcessId; }
 | 
						|
 | 
						|
  bool ResumeAndWaitUntilExit() {
 | 
						|
    if (::ResumeThread(mChildMainThread) == 0xffffffff) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "ResumeThread failed - %08lx\n",
 | 
						|
          GetLastError());
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (::WaitForSingleObject(mChildProcess, 60000) != WAIT_OBJECT_0) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "Unexpected result from WaitForSingleObject\n");
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    DWORD exitCode;
 | 
						|
    if (!::GetExitCodeProcess(mChildProcess, &exitCode)) {
 | 
						|
      printf(
 | 
						|
          "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
          "GetExitCodeProcess failed - %08lx\n",
 | 
						|
          GetLastError());
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    return exitCode == 0;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
HMODULE ChildProcess::sExecutableImageBase = 0;
 | 
						|
volatile const DWORD ChildProcess::sReadOnlyProcessId = 0;
 | 
						|
 | 
						|
int wmain(int argc, wchar_t* argv[]) {
 | 
						|
  printf("Process: %-8lx Base: %p\n", ::GetCurrentProcessId(),
 | 
						|
         ::GetModuleHandle(nullptr));
 | 
						|
 | 
						|
  if (argc == 2 && wcscmp(argv[1], kChildArg) == 0) {
 | 
						|
    return ChildProcess::Main();
 | 
						|
  }
 | 
						|
 | 
						|
  ChildProcess childProcess(argv[0], kChildArg);
 | 
						|
  if (!childProcess) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!TestAddString()) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  LauncherResult<HMODULE> remoteImageBase =
 | 
						|
      nt::GetProcessExeModule(childProcess);
 | 
						|
  if (remoteImageBase.isErr()) {
 | 
						|
    PrintLauncherError(remoteImageBase, "nt::GetProcessExeModule failed");
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  nt::CrossExecTransferManager transferMgr(childProcess);
 | 
						|
  if (!transferMgr) {
 | 
						|
    printf(
 | 
						|
        "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
        "CrossExecTransferManager instantiation failed.\n");
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  LauncherVoidResult result =
 | 
						|
      transferMgr.Transfer(&ChildProcess::sExecutableImageBase,
 | 
						|
                           &remoteImageBase.inspect(), sizeof(HMODULE));
 | 
						|
  if (result.isErr()) {
 | 
						|
    PrintLauncherError(result, "ChildProcess::WriteData(Imagebase) failed");
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  DWORD childPid = childProcess.GetProcessId();
 | 
						|
 | 
						|
  DWORD* readOnlyData = const_cast<DWORD*>(&ChildProcess::sReadOnlyProcessId);
 | 
						|
  result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD));
 | 
						|
  if (result.isOk()) {
 | 
						|
    printf(
 | 
						|
        "TEST-UNEXPECTED | TestCrossProcessWin | "
 | 
						|
        "A constant was located in a writable section.");
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  AutoVirtualProtect prot =
 | 
						|
      transferMgr.Protect(readOnlyData, sizeof(uint32_t), PAGE_READWRITE);
 | 
						|
  if (!prot) {
 | 
						|
    printf(
 | 
						|
        "TEST-FAILED | TestCrossProcessWin | "
 | 
						|
        "VirtualProtect failed - %08lx\n",
 | 
						|
        prot.GetError().AsHResult());
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  result = transferMgr.Transfer(readOnlyData, &childPid, sizeof(DWORD));
 | 
						|
  if (result.isErr()) {
 | 
						|
    PrintLauncherError(result, "ChildProcess::WriteData(PID) failed");
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  {
 | 
						|
    // Define a scope for |sharedSection| to resume the child process after
 | 
						|
    // the section is deleted in the parent process.
 | 
						|
    result = gSharedSection.Init(transferMgr.LocalPEHeaders());
 | 
						|
    if (result.isErr()) {
 | 
						|
      PrintLauncherError(result, "SharedSection::Init failed");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
 | 
						|
    for (const auto& testString : kTestStrings) {
 | 
						|
      nt::AllocatedUnicodeString ustr(testString);
 | 
						|
      result = gSharedSection.AddDepenentModule(ustr);
 | 
						|
      if (result.isErr()) {
 | 
						|
        PrintLauncherError(result, "SharedSection::AddDepenentModule failed");
 | 
						|
        return 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    result = gSharedSection.TransferHandle(transferMgr,
 | 
						|
                                           GENERIC_READ | GENERIC_WRITE);
 | 
						|
    if (result.isErr()) {
 | 
						|
      PrintLauncherError(result, "SharedSection::TransferHandle failed");
 | 
						|
      return 1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (!childProcess.ResumeAndWaitUntilExit()) {
 | 
						|
    return 1;
 | 
						|
  }
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 |