From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- browser/app/winlauncher/test/TestSameBinary.cpp | 255 ++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 browser/app/winlauncher/test/TestSameBinary.cpp (limited to 'browser/app/winlauncher/test/TestSameBinary.cpp') diff --git a/browser/app/winlauncher/test/TestSameBinary.cpp b/browser/app/winlauncher/test/TestSameBinary.cpp new file mode 100644 index 0000000000..2cb45f546f --- /dev/null +++ b/browser/app/winlauncher/test/TestSameBinary.cpp @@ -0,0 +1,255 @@ +/* -*- 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 +#include + +#include + +#include "SameBinary.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Assertions.h" +#include "mozilla/CmdLineAndEnvUtils.h" +#include "mozilla/NativeNt.h" +#include "mozilla/Unused.h" +#include "mozilla/Vector.h" +#include "mozilla/WinHeaderOnlyUtils.h" +#include "nsWindowsHelpers.h" + +#define EXPECT_SAMEBINARY_IS(expected, option, message) \ + do { \ + mozilla::LauncherResult isSame = \ + mozilla::IsSameBinaryAsParentProcess(option); \ + if (isSame.isErr()) { \ + PrintLauncherError(isSame, \ + "IsSameBinaryAsParentProcess returned error " \ + "when we were expecting success."); \ + return 1; \ + } \ + if (isSame.unwrap() != expected) { \ + PrintErrorMsg(message); \ + return 1; \ + } \ + } while (0) + +/** + * This test involves three processes: + * 1. The "Monitor" process, which is executed by |MonitorMain|. This process + * is responsible for integrating with the test harness, so it spawns the + * "Parent" process (2), and then waits for the other two processes to + * finish. + * 2. The "Parent" process, which is executed by |ParentMain|. This process + * creates the "Child" process (3) and then waits indefinitely. + * 3. The "Child" process, which is executed by |ChildMain| and carries out + * the actual test. It terminates the Parent process during its execution, + * using the Child PID as the Parent process's exit code. This serves as a + * hacky yet effective way to signal to the Monitor process which PID it + * should wait on to ensure that the Child process has exited. + */ + +static const char kMsgStart[] = "TEST-FAILED | SameBinary | "; + +inline void PrintErrorMsg(const char* aMsg) { + printf("%s%s\n", kMsgStart, aMsg); +} + +inline void PrintWinError(const char* aMsg) { + mozilla::WindowsError err(mozilla::WindowsError::FromLastError()); + printf("%s%s: %S\n", kMsgStart, aMsg, err.AsString().get()); +} + +template +inline void PrintLauncherError(const mozilla::LauncherResult& aResult, + const char* aMsg = nullptr) { + const char* const kSep = aMsg ? ": " : ""; + const char* msg = aMsg ? aMsg : ""; + const mozilla::LauncherError& err = aResult.inspectErr(); + printf("%s%s%s%S (%s:%d)\n", kMsgStart, msg, kSep, + err.mError.AsString().get(), err.mFile, err.mLine); +} + +static int ChildMain(DWORD aExpectedParentPid) { + mozilla::LauncherResult parentPid = mozilla::nt::GetParentProcessId(); + if (parentPid.isErr()) { + PrintLauncherError(parentPid); + return 1; + } + + if (parentPid.inspect() != aExpectedParentPid) { + PrintErrorMsg("Unexpected mismatch of parent PIDs"); + return 1; + } + + const DWORD kAccess = PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE; + nsAutoHandle parentProcess( + ::OpenProcess(kAccess, FALSE, parentPid.inspect())); + if (!parentProcess) { + PrintWinError("Unexpectedly failed to call OpenProcess on parent"); + return 1; + } + + EXPECT_SAMEBINARY_IS( + true, mozilla::ImageFileCompareOption::Default, + "IsSameBinaryAsParentProcess returned incorrect result for identical " + "binaries"); + EXPECT_SAMEBINARY_IS( + true, mozilla::ImageFileCompareOption::CompareNtPathsOnly, + "IsSameBinaryAsParentProcess(CompareNtPathsOnly) returned incorrect " + "result for identical binaries"); + + // Total hack, but who cares? We'll set the parent's exit code as our PID + // so that the monitor process knows who to wait for! + if (!::TerminateProcess(parentProcess.get(), ::GetCurrentProcessId())) { + PrintWinError("Unexpected failure in TerminateProcess"); + return 1; + } + + // Close our handle to the parent process so that no references are held. + ::CloseHandle(parentProcess.disown()); + + // Querying a pid on a terminated process may still succeed some time after + // that process has been terminated. For the purposes of this test, we'll poll + // the OS until we cannot succesfully open the parentPid anymore. + const uint32_t kMaxAttempts = 100; + uint32_t curAttempt = 0; + while (HANDLE p = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, + parentPid.inspect())) { + ::CloseHandle(p); + ::Sleep(100); + ++curAttempt; + if (curAttempt >= kMaxAttempts) { + PrintErrorMsg( + "Exhausted retry attempts waiting for parent pid to become invalid"); + return 1; + } + } + + EXPECT_SAMEBINARY_IS( + false, mozilla::ImageFileCompareOption::Default, + "IsSameBinaryAsParentProcess returned incorrect result for dead parent " + "process"); + EXPECT_SAMEBINARY_IS( + false, mozilla::ImageFileCompareOption::CompareNtPathsOnly, + "IsSameBinaryAsParentProcess(CompareNtPathsOnly) returned incorrect " + "result for dead parent process"); + + return 0; +} + +static nsReturnRef CreateSelfProcess(int argc, wchar_t* argv[]) { + nsAutoHandle empty; + + DWORD myPid = ::GetCurrentProcessId(); + + wchar_t strPid[11] = {}; +#if defined(__MINGW32__) + _ultow(myPid, strPid, 16); +#else + if (_ultow_s(myPid, strPid, 16)) { + PrintErrorMsg("_ultow_s failed"); + return empty.out(); + } +#endif // defined(__MINGW32__) + + wchar_t* extraArgs[] = {strPid}; + + auto cmdLine = mozilla::MakeCommandLine( + argc, argv, mozilla::ArrayLength(extraArgs), extraArgs); + if (!cmdLine) { + PrintErrorMsg("MakeCommandLine failed"); + return empty.out(); + } + + STARTUPINFOW si = {sizeof(si)}; + PROCESS_INFORMATION pi; + BOOL ok = + ::CreateProcessW(argv[0], cmdLine.get(), nullptr, nullptr, FALSE, + CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &si, &pi); + if (!ok) { + PrintWinError("CreateProcess failed"); + return empty.out(); + } + + nsAutoHandle proc(pi.hProcess); + nsAutoHandle thd(pi.hThread); + + return proc.out(); +} + +static int ParentMain(int argc, wchar_t* argv[]) { + nsAutoHandle childProc(CreateSelfProcess(argc, argv)); + if (!childProc) { + return 1; + } + + if (::WaitForSingleObject(childProc.get(), INFINITE) != WAIT_OBJECT_0) { + PrintWinError( + "Unexpected result from WaitForSingleObject on child process"); + return 1; + } + + MOZ_ASSERT_UNREACHABLE("This process should be terminated by now"); + return 0; +} + +static int MonitorMain(int argc, wchar_t* argv[]) { + // In this process, "parent" means the process that will be running + // ParentMain, which is our child process (confusing, I know...) + nsAutoHandle parentProc(CreateSelfProcess(argc, argv)); + if (!parentProc) { + return 1; + } + + if (::WaitForSingleObject(parentProc.get(), 60000) != WAIT_OBJECT_0) { + PrintWinError("Unexpected result from WaitForSingleObject on parent"); + return 1; + } + + DWORD childPid; + if (!::GetExitCodeProcess(parentProc.get(), &childPid)) { + PrintWinError("GetExitCodeProcess failed"); + return 1; + } + + nsAutoHandle childProc(::OpenProcess(SYNCHRONIZE, FALSE, childPid)); + if (!childProc) { + // Nothing to wait on anymore, which is OK. + return 0; + } + + // We want no more references to parentProc + ::CloseHandle(parentProc.disown()); + + if (::WaitForSingleObject(childProc.get(), 60000) != WAIT_OBJECT_0) { + PrintWinError("Unexpected result from WaitForSingleObject on child"); + return 1; + } + + return 0; +} + +extern "C" int wmain(int argc, wchar_t* argv[]) { + if (argc == 3) { + return ChildMain(wcstoul(argv[2], nullptr, 16)); + } + + if (!mozilla::SetArgv0ToFullBinaryPath(argv)) { + return 1; + } + + if (argc == 1) { + return MonitorMain(argc, argv); + } + + if (argc == 2) { + return ParentMain(argc, argv); + } + + PrintErrorMsg("Unexpected argc"); + return 1; +} -- cgit v1.2.3